How to fix EConvertError handling in Delphi 7? - delphi

so i tried to make simple app to check if entered number is odd or even. Also i wanted to handle EConvertError by entering Try and Except.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
var x:integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
try
x:=StrToInt(InputBox('Zadávanie','Napíš číslo',''));
if x mod 2 = 0 then ShowMessage('Zadané číslo je párne')
else ShowMessage('Zadané číslo je nepárne');
except
on E: EConvertError do
ShowMessage('Zadávaj len čísla!');
end;
end;
end.
But this don't work and still showing the same exact Project1.exe raised exception class EConvertError with message ''' is not a vaild integer value instead of 'Zadávaj len čísla!'. Why?

Your try..except code is fine.
You are simply experiencing what happens when you run your app inside of the IDE's debugger. The debugger sees the exception before your app does. You are seeing a popup message from the debugger. Simply dismiss the popup and either press the "Run" button in the IDE, or press F9 on the keyboard, to continue execution and the exception will be passed to your app for normal handling, calling your except block. The popup will not happen when you run your app outside of the debugger, the except will just be called immediately.
If you don't want the debugger to popup a message on the exception, you can add EConvertError to the debugger's list of exceptions that it ignores. Or you can place breakpoints around the code that instruct the debugger to ignore exceptions for just this block of code.
Or, you can simply use TryStrToInt() instead of StrToInt(). TryStrToInt() does not raise an exception on a conversion error.

Just use TryStrToInt() which returns false if the input was no valid integer, something like that:
procedure TForm1.FormCreate(Sender: TObject);
var x: integer;
begin
try
if not TryStrToInt(InputBox('Zadávanie','Napíš číslo',''),x) then begin
ShowMessage('Zadávaj len čísla!');
exit;
end;
if x mod 2 = 0 then ShowMessage('Zadané číslo je párne')
else ShowMessage('Zadané číslo je nepárne');
end;
So no exception will be raised on input error.
IMHO exceptions should be exceptional - I don't like using EConvertError at all in my code.
BTW it is not a very good idea to put some UI code in the OnCreate event - better use OnShow for that.

Related

Borland Delphi: How do I debug this program?

I have to write program in Delphi using VCL forms. Three figures which are square, hexagon and octagonal must move to up border, then to bottom border and so on. The problem is that my program freezes, when I'm trying to put values in condition operators to stop moving, if coordinate Y = 0. Though it works(strangely) if I put value = 180, for example.
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TMainForm = class(TForm)
Image: TImage;
BeginButton: TButton;
EndButton: TButton;
Timer1: TTimer;
Edit1: TEdit;
procedure FormActivate(Sender: TObject);
procedure BeginButtonClick(Sender: TObject);
procedure EndButtonClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses Figure;
{$R *.dfm}
Var
t:single=0.0;
L:TSquare;
S:THexagon;
C:TOctagon;
Moving:Boolean=true;
procedure TMainForm.FormActivate(Sender: TObject);
begin
Image.Canvas.Brush.Color:=clWhite;
end;
procedure TMainForm.Timer1Timer(Sender: TObject);
begin
L.Move(t);
S.Move(-0.2*t);
C.Move(0.5*t);
t:=t+0.5;
end;
procedure TMainForm.BeginButtonClick(Sender: TObject);
begin
L:=TSquare.Create(60,35,Image);
S:=THexagon.Create(180,100,Image);
C:=TOctagon.Create(300,100,Image);
Timer1.Enabled:=true;
end;
procedure TMainForm.EndButtonClick(Sender: TObject);
begin
Close;
end;
initialization
finalization
L.Free;
S.Free;
C.Free;
end.
And second Unit:
Unit Figure;
Interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
Type
TFigure=Class
private x,y, b,
dx:integer;
Image:TImage;
procedure Draw;virtual;abstract;
procedure Rel(t:real);virtual;
public
constructor Create(ax,ay:integer;aImage:TImage);
procedure Move(t:single);
end;
THexagon=Class(TFigure)
private procedure Draw;override;
end;
TSquare=Class(TFigure)
private procedure Draw;override;
end;
TOctagon=Class(TFigure)
private procedure Draw;override;
end;
Implementation
Constructor TFigure.Create;
Begin
inherited Create;
x:=ax; y:=ay; Image:=aImage;
End;
Procedure TFigure.Rel;
Begin
dx:=5*round(t);
End;
Procedure TFigure.Move;
Begin
Image.Canvas.Pen.Color:=clWhite;
Draw;
Image.Canvas.Pen.Color:=clBlack;
Rel(t);
Draw;
End;
Procedure TSquare.Draw;
Begin
b:=70;
Image.Canvas.MoveTo(x+round(0.5*b),y-round(0.5*b));
Image.Canvas.LineTo(x-round(0.5*b),y-round(0.5*b));
Image.Canvas.LineTo(x-round(0.5*b),y+round(0.5*b));
Image.Canvas.LineTo(x+round(0.5*b),y+round(0.5*b));
Image.Canvas.LineTo(x+round(0.5*b),y-round(0.5*b));
End;
Procedure THexagon.Draw;
Begin
b:=70;
repeat
begin
Image.Canvas.MoveTo(x+round(0.5*b),y+dx);
Image.Canvas.LineTo(x+round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.5*b),y+dx);
Image.Canvas.LineTo(x-round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.5*b),y+dx);
end;
until ((y+round(0.5*b)+dx)<180);
End;
Procedure TOctagon.Draw;
var
I: Integer;
p: array[1..9] of tpoint;
u:extended;
Begin
x:=300;
y:=100;
u:=0;
for I := 1 to 8 do
begin
p[i].X:=x+round(40*cos(u));
p[i].Y:=y-round(40*sin(u));
u:=u+pi/4;
end;
repeat
begin
Image.Canvas.MoveTo(p[8].x,p[8].y-dx);
for I := 1 to 8 do
Image.Canvas.LineTo(p[i].X,p[i].y-dx);
end;
until (p[3].y>50);
End;
end.
Delphi comes with an integrated debugger. You should use it. Here's how to start investigating a case where a program seems to hang.
Start your program under control of the debugger with the "play" button.
Reproduce the situation you're trying to investigate.
When the program hangs, switch to the debugger and press the "pause" button. The debugger will interrupt the execution of your program so you can investigate the current state.
Look at the call stack. (If the call-stack window isn't already visible, you can show it by using the "debug windows" menu option in the IDE.)
The call stack will show the list of functions your program has called. At the top of the stack will be the function your program was running at the moment you paused. The function below it will be the function that called the current function, and so on until you reach the bottom of the stack, which represents the main function of your program.
The function you stop in probably won't be one you wrote. Instead, it's usually a function provided by the OS or by the Delphi run-time library. You don't want to debug those. Generally, you can assume they already work properly. You're looking for a bug in your code instead.
Use the "run until return" command to let the topmost function continue running. Repeat that until you reach one of your functions on the call stack. That's probably the culprit.
Now that you've identified the problematic function, it's time to investigate it further.
Use the "step over" debugger command to run each line of your function one by one. (There's also a "step into" command, but that will step into functions that aren't yours, and you're not interested in those now.)
Observe the current values of variables in your code. You can hover the mouse over a variable to let the debugger display its value in a tool tip, or you can use the "watches" debug window to display multiple variables at once. They'll be updated after each step in your program.
Pay attention to the variables' values. You should already have some expectation of how their values should progress over the course of your program. You probably thought about that progression while you were writing the code. Think back to that time and compare the results you observe in the debugger with your previous expectations. Do they match? If so, then keep stepping through the code. If they don't match, though, then you've found a bug. Fix it.
Another source of unexpected behavior is to reach a point in your program that you didn't expect to reach. Maybe the program called a function it shouldn't have, or maybe you've executed a loop more times you wanted to. If you can work out the reason, then fix the bug. Otherwise, you might need to back up a little ways.
Identify a point in your program earlier than where you have observed the unexpected behavior. Look for the blue dots in the left margin of the code editor. Those dots represent places where you can set a breakpoint. Click one of the dots, and you should notice the line be highlighted (probably in red).
Terminate your program, and run it again.
This time, you should see the debugger stop before the program appears to hang because execution will have reached the breakpoint first. The debugger interrupts your program there.
Step through the lines of your code as you did before, and watch for the condition that causes your program to veer from the expected path of execution. When you've identified the bug, fix it.
It freezes because your repeat-until loop will never end.
repeat
begin
Image.Canvas.MoveTo(x+round(0.5*b),y+dx);
Image.Canvas.LineTo(x+round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.5*b),y+dx);
Image.Canvas.LineTo(x-round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.5*b),y+dx);
end;
until ((y+round(0.5*b)+dx)<180);
Its condition is based on y, b and dx values but they never change in your loop.
To confirm where it hangs, use the Pause command in Delphi, then press F7/F8 to run it step by step.

Populating an array and displaying its contents into a rich edit on a different form

This is the error I get when I click the btnInfoClick
Debugger Exception Notification
Project_PAT_Phase_3.exe raised exception class EAccessViolation with message 'Access violation at address 004047E0 in module 'Project_PAT_Phase_3.exe' 'Read of address 00000022'.
The program runs smoothly without any errors until I click the button as shown in my code. Please I would appreciate your help.
unit Navigation;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, InfoPopUp;
type
Tvector = Array[1..14] of string;
TFrmNavigation = class(TForm)
btnVote: TButton;
RdgInfo: TRadioGroup;
Label2: TLabel;
btnInfo: TButton;
procedure btnInfoClick(Sender: TObject);
private
public
MyFile : TextFile;
sLine : string;
sArrayParty : Tvector;
end;
var
FrmNavigation: TFrmNavigation;
implementation
procedure TFrmNavigation.btnInfoClick(Sender: TObject);
var
K : integer;
iCheck : integer;
begin
FrmInfo.Visible := true;
K := 1;
iCheck := 0;
if FileExists('PartyInfo.txt') <> True
then
begin
MessageDlg('File does not exist',mtError,[mbOK],0);
Exit;
end;// end of If statement
AssignFile(MyFile,'PartyInfo.txt');
Reset(MyFile);
while NOT eof(MyFile) do
begin
Inc(K);
Readln(MyFile,sLine);
sLine := sArrayParty[K];
end;//end of While
closefile(MyFile);
case RdgInfo.ItemIndex of
0 : begin
FrmInfo.Caption := 'African Christian Democratic Party (ACDP)';
FrmInfo.redOutput.Text := sArrayParty[1];
end;
1 : begin
FrmInfo.Caption := 'African National Congress (ANC)';
FrmInfo.redOutput.Text := sArrayParty[2];
end;
end;
the last end. below is where the error pops up in the code but its in the project unit which is weird cause when i had a breakpoint the exception would stop the program at the while loop.
program PAT_Phase_3;
uses
Forms,
WelcomePage in 'WelcomePage.pas' {frmWP},
Navigation in 'Navigation.pas' {FrmNavigation},
InfoPopUp in 'InfoPopUp.pas' {FrmInfo};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TfrmWP, frmWP);
Application.CreateForm(TFrmNavigation, FrmNavigation);
Application.CreateForm(TFrmInfo, FrmInfo);
Application.Run;
end.
The error message, with an address so close to zero, indicates that you are accessing a nil object reference. Most likely FrmInfo is nil. Or perhaps sArrayParty is nil. Use the debugger to confirm where the error is. Obviously it's an error to refer to a nil reference.
When you get an error like this, use the debugger, configured to break on exceptions, to point you at the line of code that faults. Then try to work out why that line of code fails.
If you are presented with hundreds of lines of code, it's hard to work out where a fault is. If you can concentrate on a single line of code, it's much easier. Time for you to learn to debug.
In Delphi you can set forms to AutoCreate or not via a project setting. If your project is not auto creating all forms, then your FrmInfo is going to be nil. Add this code before your
FrmInfo.Visible := true; line and see if it gets you further.
if FrmInfo = nil then
FrmInfo := TFrmInfo.Create(nil);

Proper Catastrophic Error Handling

There's something I keep running into that I really haven't solved with Delphi programs and was wondering if anyone could instruct me on it. As the topic says, how do you do proper catastrophic error handling? For instance:
// is file necessary for the program present?
if not FileExists(FilePath1) then
begin
raise Exception.Create(FilePath1 + ' does not exist and is required for this program to function.');
// I obviously need to do something here to make the program QUIT and not have
// any more code run.
Application.Terminate;
Abort;
end;
I can use the exception unit there as well and throw out an exception, but the program continues as before. I've used the halt call in the past, but it seems to not do any cleanup or the like so I end up making a big procedure with close and free calls for everything I've done just to be sure (and even then I'm not sure of any of the behind the scenes stuff).
So what is the right way to handle such things?
Edit: To clarify, I'm wanting to know about how to make the program do what clean-up it needs to do and then EXIT NOW and not do any other code.
To perform abnormal termination call Halt() passing the exit code.
if CatastropicErrorDetected then
begin
... show error message
Halt(1);
end;
On Windows this results in a call to TerminateProcess and will stop execution there and then.
You note that no cleanup is performed and usually that's what you want. Since you are performing this check at application startup there should be nothing to cleanup.
IMHO the only clean way would be checking for "Fatal conditions" before Application is running.
program Project2;
uses
Forms,Dialogs,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
ReportMemoryLeaksOnShutDown := true;
Application.Initialize;
if True then // your condition here
begin
MessageDLG('Fatal Error',mtError,[mbok],0);
end
else
begin
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
Any other approach will have side effects
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen }
FSL:TStringList;
public
Destructor Destroy;override;
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
destructor TForm1.Destroy;
begin
FreeAndNil(FSL);
Showmessage('Will never be seen with Application.Terminate + HALT');
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
const
Testing=0; // try 1 and 2 too
begin
FSL:=TStringList.Create;
Try
raise Exception.Create('Terminating now');
except
case Testing of
0: begin
// exception object will not be freed other code will be prevented, Form won't be shown
Application.Terminate;
HALT;
end;
1: begin
// exception object will not be freed Form won't be shown
HALT;
end;
2: begin
// clean but Form will be shown
Application.Terminate;
end;
end;
end;
end;
end.
You can instruct the application global object to terminate the program by calling Application.Terminate.
Call Terminate to end the application programmatically. By calling Terminate rather than freeing the application object, you allow the application to shut down in an orderly fashion.
Terminate calls the Windows API PostQuitMessage function to perform an orderly shutdown of the application. Terminate is not immediate.
Since the call can occur deeper in the stack, you can also raise an Exception, and you code your program to not handle it in order to let the execution reach the main application loop and the default exception handler catch it.
That way, you effectively prevent's more code to run in your application.
In code it may look like this:
if not FileExists(FilePath1) then
begin
MessageDlg(FilePath1 + ' does not exist and is required for this program to function.',
mtWarning, [mbOK], 0);
Application.Terminate;
Abort; //raising a EAbort just as an example
end;
Depending on where this code is called, I advise you not to show the message directly, but rather raise an exception with the message and let the application object default HandleException method show the message for you:
if not FileExists(FilePath1) then
begin
Application.Terminate;
raise EMyFatalException.Create(FilePath1
+ ' does not exist and is required for this program to function.');
end;
Which looks more natural to me. EMyFatalException is a hypothetical exception class you can declare and never handle in your except clauses.
You can write your own Application.OnException handler, ex:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure HandleException(Sender: TObject; E: Exception);
end;
var
Form1: TForm1;
type
EMyFatalError = class(Exception);
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Application.OnException:= HandleException;
raise EMyFatalError.Create('OOPS!');
end;
procedure TForm1.HandleException(Sender: TObject; E: Exception);
begin
Application.ShowException(E);
if E is EMyFatalError then
Application.Terminate;
end;
end.

Why don't I get an access violation when I call methods on an uninitialized function result?

One of my coworkers show me a code written in Delphi-XE XE Version 15.0.3953.35171, which I believe it should raise an access violation. The code is bellow:
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm3 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
function test:TstringList;
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
var aStrList : TStringList;
begin
aStrList := TStringList.Create;
test;
FreeAndNil(aStrList);
end;
function TForm3.test: TstringList;
var i:Integer;
begin
for i:=0 to 1000 do
Result.Add('aaa');//AV?
end;
end.
Inspecting aStrList and Result has the following results:
aStrList: TStringList $12FEDC : $42138A
Result: TStringList $12FEC4 : $B01B90
I do not understand why it is working. Result.Add should raise an access violation
LE: It seems that is working only on Debug Build Configuration.
The Result variable in that function has not been initialized and could hold any value. Now, the implementation detail means that, in some combinations of compiler options, your code happens to run with Result referring to a valid object. But that's really just a coincidence of those implementation details.
If this were C++ then that function would exhibit undefined behaviour. Although that term does not have a formal meaning in Delphi, it can be helpful to use that term in a Delphi setting to mean the same thing as in the context of C++.
I would also make the point that even if Result did not refer to a valid string list object, your code would not be guaranteed to raise an access violation. It could be that Result points to a block of memory that just happens to look enough like a string list for that code to execute successfully.
If you do things properly, you can predict the behaviour of your program. If your code is flawed and induces undefined behaviour, then your program's behaviour becomes unpredictable. It may work. It may fail. Or that code may execute fine, but then lead to a failure later in the program's execution. And so on.

How to avoid an error from displaying?

How to avoid an error from displaying the little Windows error box?
Try and Except dont work because the error isnt showned by Delphi but from Program or I think from Windows.
try
Size:=TFileStream.Create(BitFile,fmOpenRead);
except on E: EFCreateError
do EC.Add('Error: ' + IntToStr(GetLastError));
end;
Is the error shown in your application? Otherwise put, is it an unhandled exception? Or is it a box displayed by Windows or by an external application?
You say 'event', but event handlers can contain try..except blocks too.
If it is an exception, and you don't know where it's coming from, you can use the TApplicationEvents class to attach the Application.OnException event. It will fire on all unhandled exceptions. There you can catch it, or rather, set a breakpoint and use the stack trace to see where the exception is coming from.
An error box doesn't imply an exception has been raised. An error box can be explicitly shown in code.
So, it seems your question is "How can I prevent 3rd party code from working As Designed?". Beside decompiling the binaries, I'm afraid I can't suggest much, especially if you don't have the source.
If you have the source code and know the routine that needs to be replaced, you could write your own replacement and "hijack" the routine at runtime. This is the method used by, for example, the fastcode project to replace delphi's routine without recompiling the VCL. You can see the implementation in their project.
http://fastcode.sourceforge.net/
Unit: FastcodePatch.pas
Here it is
private
{ Private declarations }
public
procedure MyExceptionHandler(Sender : TObject; E : Exception ); //define exception handler
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.MyExceptionHandler(Sender:TObject;E:Exception);
begin
//Do nothing
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := MyExceptionHandler;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
//Generate an exception
asm
mov eax,8272
mov [eax],$2FFFFF
end
end;

Resources