I have a very basic and simple class like this:
unit Loader;
interface
uses
Vcl.Dialogs;
type
TLoader = Class(TObject)
published
constructor Create();
end;
implementation
{ TLoader }
constructor TLoader.Create;
begin
ShowMessage('ok');
end;
end.
And from Form1 i call it like this:
procedure TForm1.Button1Click(Sender: TObject);
var
the : TLoader;
begin
the := the.Create;
end;
Now, just after the the := the.Create part, delphi shows the message with 'ok' and then gives me an error and says Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x0040559d: read of address 0xffffffe4'.
Also it shows this line:
constructor TLoader.Create;
begin
ShowMessage('ok');
end; // <-------- THIS LINE IS MARKED AFTER THE ERROR.
I am new at delphi. I am using Delphi XE2 and i couldnt manage to fix this error. Does anyone show me a path or have solution for this?
var
the : TLoader;
begin
the := the.Create;
is incorrect. It should be
var
the : TLoader;
begin
the := TLoader.Create;
You've got the syntax wrong. If you're constructing a new object, you should use the class name, not the variable name, in the constructor call:
procedure TForm1.Button1Click(Sender: TObject);
var
the : TLoader;
begin
the := TLoader.Create;
end;
Related
The below class implementation works fine in Delphi, however it fails when running in Lazarus.
The purpose is to run a timer asynchronously.
// Declaration
type
TTimerEvents = class
private
class procedure tonTimer(Sender: TObject);
end;
// Implementation
class procedure TTimerEvents.tonTimer(Sender: TObject);
var
vTag: string;
begin
(Sender as TTimer).Enabled := false;
vTag := 'Tag_' + IntToStr((Sender as TTimer).Tag);
// more stuff here
end;
// Call
procedure TmainForm.actTON(i: integer);
var
t: TTimer;
begin
t := TTimer.Create(mainForm);
t.Interval := StrToInt(vTime) * 1000;
t.Tag := vTag;
t.OnTimer := TTimerEvents.tonTimer; //<<<----HERE
t.Enabled := True;
// more stuff here
end;
Error msg:
fmmain.pas(371,33) Error: Wrong number of parameters specified for call to "tonTimer"
If I include Timer1 as Sender then I get a different error msg:
fmmain.pas(371,49) Error: Incompatible type for arg no. 1: Got "untyped", expected "<procedure variable type of procedure(TObject) of object;Register>"
From the errors you are probably testing Delphi code without enabling full Delphi compatibility mode with {$mode delphi}.
I want to instantiate classes based on a parameter. Both classes are derived from TSample so I define my code as:
var T: TSample;
then I do
T := TMySample.Create;
or
T := TYourSample.Create;
and calling T.Hello gives an "Abstract Error".
type TSample = class
public
procedure Hello; virtual; abstract;
end;
TMySample = class(TSample)
public
procedure Hello;
end;
TYourSample = class(TSample)
public
procedure Hello;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var T: TSample;
a: Integer;
begin
if a = 1 then T := TMySample.Create
else T := TYourSample.Create;
T.Hello; //Abstract Error here
T.Free;
end;
procedure TMySample.Hello;
begin
showmessage('My');
end;
procedure TYourSample.Hello;
begin
showmessage('Your');
end;
You forgot to declare the overridden methods as, well, overridden:
TMySample = class(TSample)
public
procedure Hello; override; // <--
end;
TYourSample = class(TSample)
public
procedure Hello; override; // <--
end;
Actually, the compiler warned you about this, but you didn't listen :)
[dcc32 Warning] Unit1.pas(25): W1010 Method 'Hello' hides virtual method of base type 'TSample'
[dcc32 Warning] Unit1.pas(30): W1010 Method 'Hello' hides virtual method of base type 'TSample'
Also, you probably already know this, but there are two issues with your sample code:
Since local variables of non-managed types are not initialized, the value of a is undefined.
You don't protect the TSample object, so you might leak resources. (In fact, in this case, you will due to the exception!)
Fixed:
a := 123;
if a = 1 then
T := TMySample.Create
else
T := TYourSample.Create;
try
T.Hello; //Abstract Error here
finally
T.Free;
end;
I need to include an error code on exception.
Exceptions (Delphi)
type EInOutError = class(Exception)
ErrorCode: Integer;
end;
But I have no idea how to set the error code.
I tried:
type ECustomError= class(Exception)
ErrorCode: Integer=129;
end;
But no success, how can I set the error code?
You cannot (and should not) set this in the class' definition. There is no context here about where and why it's being called. Instead, you need to assign this in run-time, at any place where this exception may be raised.
This can be done by deriving your class from EInOutError and adding a custom constructor to it:
type
ECustomError = class(EInOutError)
public
constructor Create(AMsg: String; ACode: Integer); reintroduce;
end;
constructor ECustomError.Create(AMsg: String; ACode: Integer);
begin
inherited Create(AMsg);
ErrorCode := ACode;
end;
Then, when you raise the exception, you call it like so...
raise ECustomError.Create('Some error message', 129);
You could go a bit further and add this code to your message...
constructor ECustomError.Create(AMsg: String; ACode: Integer);
begin
inherited CreateFmt('%s (Error Code %d)', [AMsg, ACode]);
ErrorCode := ACode;
end;
I wonder if I have found an Embarcadero compiler bug ...
The problem looks like it is related to generics.
Here is my source code
unit u_DateCount;
interface
uses
SysUtils,
u_JavaScriptable
;
type
TDateCount = class (TJavaScriptable)
strict private
public
NoOfSamples : Integer;
TheDate : TDate;
function ToString():String; override;
end;
implementation
function TDateCount.ToString():String;
var
myYear, myMonth, myDay : Word;
begin
DecodeDate(TheDate, myYear, myMonth, myDay);
Result := Format('[new Date(%d, %d ,0), %d]', [myYear, myMonth, NoOfSamples]);
end;
end.
unit u_Javascriptable;
interface
type
TJavaScriptable = class
strict private
public
function ToString:String; override;
end;
implementation
function TJavaScriptable.ToString:String;
begin
Result := '';
end;
end.
unit u_LineChart;
interface
uses
System.IOUtils,
SysUtils,
System.Generics.Collections,
u_JavaScriptable
;
type
TLineChart<RecordType : TJavaScriptable> = class
strict private
Template : String;
function ConvertRecordsToString():String;
public
Records : TList<RecordType>;
function ToString():String;
constructor Create(templatePath : String);
destructor Destroy(); override;
end;
implementation
function TLineChart<RecordType>.ConvertRecordsToString():String;
var
I: Integer;
begin
//Open brackets
Result := '[ ';
//The first record
if Records.Count > 0 then
begin
Result := Result + Records[0].ToString();
end;
//Loop over records
for I := 1 to Records.Count - 1 do
begin
Result := Result + ', ' + Records[I].ToString();
end;
//Close bracket
Result := Result + ' ]';
end;
function TLineChart<RecordType>.ToString():String;
begin
Result := Format(Template, [ConvertRecordsToString()]);
end;
constructor TLineChart<RecordType>.Create(templatePath : String);
begin
inherited Create();
Template := TFile.ReadAllText(templatePath);
Records := TList<RecordType>.Create();
end;
destructor TLineChart<RecordType>.Destroy();
var
I: Integer;
begin
if Assigned(Records) then
begin
for I := 0 to Records.Count - 1 do
begin
Records[I].Destroy();
end;
Records.Clear();
Records.Destroy();
Records := nil;
end;
inherited;
end;
end.
And finally the main program
program Project4;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
u_Javascriptable in 'u_Javascriptable.pas',
u_LineChart in 'u_LineChart.pas',
u_DateCount in 'u_DateCount.pas';
var
lineChart : TLineChart<TDateCount>;
begin
lineChart := TLineChart<TDateCount>.Create('linechart.html');
try
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
The error message I get when I try to compile this is
[dcc32 Fatal Error] Project4.dpr(30): F2084 Internal Error:
AV097530AC-R00000014-0
Usually when I see an error message similar to this, I can fix it by closing the embarcadero IDE and restarting it. However this did not seem to work this time.
The problem is in the implementation of TLineChart<RecordType>.Destroy().
Change Records[I].Destroy(); to Records[I].Free(); and it works.
Or you just do it correct and use TObjectList<RecordType>.Create; in the constructor which takes care of destroying all elements in it when destroying the list.
Never call Destroy directly. Use Free. While it should not result in a compiler error it is wrong anyway.
If the compiler reports an "internal error," that's always a compiler bug. You should open a ticket in QC for this. Hopefully they can get it fixed for XE5.
Since this works in XE3 but not XE4, I'm going to presume this is an XE4 bug. Until this is fixed, the solution is to use a different version of the compiler such as XE3.
Why EAccessViolation is raised when executing the code below?
uses
Generics.Collections;
...
var
list: TList<TNotifyEvent>;
...
begin
list := TList<TNotifyEvent>.Create();
try
list.Add(myNotifyEvent);
list.Remove(myNotifyEvent); // EAccessViolation at address...
finally
FreeAndNil(list);
end;
end;
procedure myNotifyEvent(Sender: TObject);
begin
OutputDebugString('event'); // nebo cokoliv jineho
end;
It looks like a bug.
If you compile with debug dcu's (normally don't do that unless you want to loose your sanity!) you see that a call to the comparer went wrong. A (possibly optional) third value of a compare function is not set and causes the access violation.
So possibly you can't put method pointers in a generic list.
Ok the following works:
uses
Generics.Defaults;
type
TForm4 = class(TForm)
...
private
procedure myNotifyEvent(Sender: TObject);
end;
TComparer<T> = class (TInterfacedObject, IComparer<T>)
public
function Compare(const Left, Right: T): Integer;
end;
implementation
uses
Generics.Collections;
var
list: TList<TNotifyEvent>;
begin
list := TList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Create);
try
list.Add(myNotifyEvent);
list.Remove(myNotifyEvent);
finally
FreeAndNil(list);
end;
end;
procedure TForm4.myNotifyEvent(Sender: TObject);
begin
ShowMessage('event');
end;
{ TComparer<T> }
function TComparer<T>.Compare(const Left, Right: T): Integer;
begin
Result := 0;
end;
You have to define your own comparer, with possiby some more intelligence ;-).
Access Violation is caused by missing comparer. I suspect this was fixed in a patch but the problem still persists (at least in Delphi 2009) if you use a TObjectList so I'm just updating with the simplest solution:
TList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Default);
or in my case
TObjectList<TNotifyEvent>.Create(TComparer<TNotifyEvent>.Default);
Is it possible to pass a custom comparer to TList<T>? I don't have D2009 in front of me, so can't try it.
the above code is used in TForm1 ...
uses
Generics.Collections;
procedure TForm1.Button1Click(Sender: TObject);
var
list: TList<TNotifyEvent>;
begin
list := TList<TNotifyEvent>.Create();
try
list.Add(myNotifyEvent);
list.Remove(myNotifyEvent); // EAccessViolation at address...
finally
FreeAndNil(list);
end;
end;
procedure TForm1.myNotifyEvent(Sender: TObject);
begin
OutputDebugString('event'); // nebo cokoliv jineho
end;