Using any exceptions to trigger code in Delphi - delphi

Hi I am new to using Delphi and am trying to write an application that will check to see if a website is up or if there is any thing wrong with it. I am using Indy's IdHTT. The problem is that it will catch any protocol errors but not things like socket errors.
procedure TWebSiteStatus.Button1Click(Sender: TObject);
var
http : TIdHTTP;
url : string;
code : integer;
begin
url := 'http://www.'+Edit1.Text;
http := TIdHTTP.Create(nil);
try
try
http.Head(url);
code := http.ResponseCode;
except
on E: EIdHTTPProtocolException do
code := http.ResponseCode;
end;
ShowMessage(IntToStr(code));
if code <> 200 then
begin
Edit2.Text:='Something is wrong with the website';
down;
end;
finally
http.Free();
end;
end;
I am basically trying to catch any thing that is not that the website is ok so I can call another form that will setup an email to tell me that the site is down.
update: First you are right I did miss that 'then' sorry about that was removing other code and it got deleted by mistake. I did not know the specific to general when dealing with exceptions thank you. Finally I did find what i was looking for was this code here
on E: EIdSocketError do
using the uses IdStack

Change your code to either catch all exceptions, or add more specific ones as well:
url := 'http://www.'+Edit1.Text;
http := TIdHTTP.Create(nil);
try
try
http.Head(url);
code := http.ResponseCode;
except
on E: EIdHTTPProtocolException do
begin
code := http.ResponseCode;
ShowMessage(IntToStr(code));
if code <> 200
begin
Edit2.Text:='Something is wrong with the website';
down;
end;
end;
// Other specific Indy (EId*) exceptions if wanted
on E: Exception do
begin
ShowMessage(E.Message);
end;
end; // Added missing end here.
finally
http.Free();
end;
Note that if you're going to handle multiple exception types, it's important to go from most specific to least specific. In other words, if you put less specific (more general types) exceptions first, this is what happens:
try
DoSomethingThatCanRaiseAnException();
except
on E: Exception do
ShowMessage('This one fires always (covers all exceptions)');
on E: EConvertError do
ShowMessage('This one will never happen - never gets this far');
end;
This one will work properly, because it's more specific to less specific. Properly, it would be reversed:
try
DoSomethingThatCanRaiseAnException();
except
on E: EConvertError do
ShowMessage('This one gets all EConvertError exceptions');
on E: Exception do
ShowMessage('This one catches all types except EConvertError');
end;

Related

How to check if TIdHTTP exception is raised

I'm not sure if this is the best way to check if a TIdHTTP exception is raised, here is what I did:
HTTP := TIDHttp.Create(nil);
try
try
HTTP.Head(URL);
SizeF := HTTP.Response.ContentLength;
code := HTTP.ResponseCode;
except
on E: Exception do
begin
code := HTTP.ResponseCode;
ShowMessage(IntToStr(code));
end;
end;
finally
HTTP.Free;
end;
if code = 200 then
// go download the file using multiple threads.
What I want to achieve is raising an axception is case there is one (I guess I already did) otherwise the program keeps running and download the file.
So is this the correct way to do it? Thanks for your replies.
As a common rule: Only handle exceptions when you can and want to handle them.
Otherwise let them just flow to stop the current execution of your code. Without any interception you will just receive a dialog with the exception message (this is handled by TApplication).
In this case you can change your code to
HTTP := TIDHttp.Create(nil);
try
HTTP.Head(URL);
// if an exception is raised then the rest of the code will not be executed
// yes, the code in finally part will execute
SizeF := HTTP.Response.ContentLength;
code := HTTP.ResponseCode;
finally
HTTP.Free;
end;
// check if all conditions are met
if code <> 200 then
// if not, raise a custom exception as you like
raise Exception.Create( 'Not what I expected here' );
// go download the file using multiple threads.

Delphi and indy TIDHTTP : distinguish between "server not found" and "not found" response error

I am using indy TIDHTTP to code a way to know whether my server on the internet is down or the address of the page on the same server is not available.
I copied the suggestion given in another thread on stackoverflow:
try
IdHTTP1.Get(mypage_address);
except
on E: EIdHTTPProtocolException do begin
if e.errorcode=404 then
showmessage('404 File not found');
// use E.ErrorCode, E.Message, and E.ErrorMessage as needed...
end;
end;
but this way I am only able to detect a server response code and not whether the server did not respond at all. I guess it's trivial but I do not know what is the way to do that?
An EIdHTTPProtocolException exception is raised when TIdHTTP successfully sends a request to the server and it sends an HTTP error reply back to TIdHTTP. If the server cannot be reached at all, a different exception (typically EIdSocketError, EIdConnectException, or EIdConnectTimeout) will be raised instead.
try
IdHTTP1.Head(mypage_address);
except
on E: EIdHTTPProtocolException do begin
ShowMessage(Format('HTTP Error: %d %s', [E.ErrorCode, E.Message]));
end;
on E: EIdConnectTimeout do begin
ShowMessage('Timeout trying to connect');
end;
on E: EIdSocketError do begin
ShowMessage(Format('Socket Error: %d %s', [E.LastError, E.Message]));
end;
on E: Exception do begin
ShowMessage(Format('Error: [%s] %s', [E.ClassName, E.Message]));
end;
end;
I attempted doing the server/site checking scientifically. but eventually simply came down to this:
function TFrameSiteChecker.GetSiteHeader(const AUrl: string): Integer;
begin
try
idhttp1.Head(AUrl);
Result := idhttp1.ResponseCode;
except
on E: exception do
Result := 0;
end;
end;
Logic being, getting the head reduces traffic, log sizes etc.
There is one one right result from the function - the return of status code 200, anything else is a fail.
Also I failed to force windows / the system / indy to not buffer/cache content, so also eventually, just run the checker every 30 minutes on a schedule. Otherwise (unless something else clears the cache) after the first connect it always succeeds, even if you unplug the machine from the network!

Try/Except/Finally not working properly in Delphi XE

I'm having this funny issue with Delphi XE where I create a try/except/finally statement and when the application generate an exception the except block is never called it jump straight to the finally block, I tried few things like invert the try/except/finally to try/finally/except, try to change the try blocks to different places, clean the code and recompile in case was a Delphi issue but noting seems to work.
What I'm trying to accomplish here is to show a dialog message to the user and after clean up the code in case of a crash.
procedure CallbackExport(Sender: TObject);
var
SaveDlg: TSaveDialog;
FileName: string;
begin
SaveDlg := TSaveDialog.Create (nil);
try
try
SaveDlg.Title := 'Export';
SaveDlg.InitialDir := GetSystemPath(CSIDL_DESKTOP);
SaveDlg.Options := [ofOverwritePrompt, ofEnableSizing];
case (Sender as TMenuItem).Tag of
cnExcel: begin
SaveDlg.Filter := 'Excel File (*.xls)|*.xls';
end;
cnHtml: begin
SaveDlg.Filter := 'HTML File (*.html)|*.html';
end;
cnTxt: begin
SaveDlg.Filter := 'Text File (*.txt)|*.txt';
end;
cnCsv: begin
SaveDlg.Filter := 'Comma Seperated File (*.csv)';
end;
cnXml: begin
SaveDlg.Filter := 'XML file (*.xml)|*.xml';
end;
end;
if not SaveDlg.Execute(self.Handle) then
Exit;
FileName := SaveDlg.FileName;
case (Sender as TMenuItem).Tag of
cnExcel: begin
ExportGridToExcel(FileName, tvdGrid);
end;
cnHtml: begin
ExportGridToHTML(FileName, tvdGrid);
end;
cnTxt: begin
ExportGridToText(FileName, tvdGrid);
end;
cnCsv: begin
ExportGridToText(FileName, tvdGrid, true, true, ',', '', '', 'CSV');
end;
cnXml: begin
ExportGridToXML(FileName, tvdGrid);
end;
end;
except
on e: exception do
begin
ShowMessage('An error occurred while saving the file ' + FileName + #13#10 + 'With a message: ' + E.Message);
StvdAudit.tvdAudit('Error saving file, reason: ' + E.Message);
end;
end;
finally
SaveDlg.Free;
end;
end
If an exception is raised inside the try/except, and not handled by code further down the call stack, it will be caught by your exception handler.
You are claiming the ExportGridToXXX is raising an exception that is not caught by the exception handler in your code. But that claim cannot be true. Either no exception is raised, or ExportGridToXXX already handles the exception.
On the more general subject of exception handling, the general policy should be not to handle them if at all possible. You should only handle them in case you need to stop the exception propagating, and need to deal with the exception at this point in the code. Normally, particularly in a UI program, you would simply let the exception be handled by the top-level exception handler.
As well as that point, you code swallows all exceptions, irrespective of their type. That's bad practice. Supposing that you do want to handle exceptions raised by ExportGridToXXX, you should only handle the exception classes that are expected. For instance, you might encounter EAccessViolation for which your app's policy is to terminate. But since you swallowed it, treating it in the same handler used to trap sharing violations, you cannot apply that policy. Always be discerning in your handling of exceptions.
Do the Export(smth) functions reside in a separate DLL? Then your application's Exception class is not the same as external DLL's Exception class.
Your exception handler is swallowing the exception, try re-raising instead:
on e: exception do
begin
StvdAudit.tvdAudit('Error saving file, reason: ' + E.Message);
raise exception.create('An error occurred while saving the file ' + FileName + #13#10 + 'With a message: ' + E.Message);
end;

FURTHER CLARIFICATION: How to correctly write Try..Finally..Except statements?

RE: How to correctly write Try..Finally..Except statements?
I'm still confused by the OP's original question. Specifically, the last line of the procedure (outside of the try..finally..end) that reads "Screen.Cursor:=crDefault".
My understanding is that any exceptions raised inside a try..except|finally..end block WILL execute the code after the "end" of the "try".
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
Screen.Cursor := crDefault;
end;
In the above example, I don't see any reason why "Screen.Cursor:=crDefault" would not be executed. Please correct me if I'm wrong.
As a further example, I've compiled this little bit of code to help illustrate. When the code is ran, THREE (3) ShowMessage() dialogs will be presented. The first "Exception Raised" and the second "finally" and the third "at end".
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
showMessage(format('%s', [12]));
except
showMessage('Exception raised');
end;
finally
showMessage('finally');
end;
showMessage('at end');
end;
So, I'm confused on why his "Screen.Cursor:=crDefault" isn't being ran, in it's original form and code. Can someone please elaborate?
The code you posted seems to work fine, because you're able to handle all possibilities. Try changing it somewhat though, so that there's an exception raised your code doesn't handle:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('42');
except
on E: EDivByZero do
ShowMessage('DivByZero');
end;
finally
ShowMessage('Finally');
end;
ShowMessage('Got here');
end;
Run this, and you'll see Finally, then the exception for 42, but no Got here message. This is because the exception took you out of the current block, the stack is unwound, and the code from the end of the finally to the end of the procedure never executes.
Move the final ShowMessage call from where it is to inside the finally and run again.
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('42');
except
on E: EDivByZero do
ShowMessage('DivByZero');
end;
finally
ShowMessage('Finally');
ShowMessage('Got here');
end;
ShowMessage('Will never get here');
end;
You'll now see both calls to ShowMessage in the finally block, one after the other, but not the one after the finally block's end;. Code inside the finally block is guaranteed to execute, while code beyond it may or may not.
To make it even more clear, the presence of the try..except block can be removed:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('42');
finally
ShowMessage('Finally');
ShowMessage('Got here');
end;
ShowMessage('Will never get here');
end;
The entire purpose of the try..finally block is to ensure that code inside the finally section will execute before the procedure ends.
In Delphi, the finally block doesn't really handle the exception that occured in the try block. It only guarantees that the code in the finally block will always be executed, whether an exception occured or not in the try block. If an exception really happened in there, it won't get caught. And when an exception didn't get caught, you know what happened to the code below it.
To catch the exception that might be occured, use the try...except... block instead. You can combine these two construct to do those two action: (1) guarantee the execution some piece of code, and (2) catch the exceptions which might occured. The common usage is like this:
try
try
// do something that might cause an exception.
finally
// do something that must be executed WHATEVER happened.
end;
except
// do something ONLY IF an exception has occured.
end;
So, you should change your code and move the Screen.Cursor := crDefault; inside the finally block. In addition, add the try...except... block to surround the try...finally... block. Like this:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
try
// do something.
finally
Obj.Free;
Screen.Cursor := crDefault;
end;
except
ShowMessage('An error has occured!');
end;
end;
Or, if you are not sure that the code Obj := TSomeObject.Create; is safe enough, you should add the second try...finally... block to surround it, like this:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
try
try
Obj := TSomeObject.Create;
try
// do something.
finally
Obj.Free;
end;
finally
Screen.Cursor := crDefault;
end;
except
ShowMessage('An error has occured!');
end;
end;
There, hope it helps :)
You don't actually catch the exception. In this case, upon exception, the "finally" block of code will execute, and then the exception will unwind the stack.

How to pass call stack information to an exception using EurekaLog?

I have a threaded application and for some purpose I want to pass call stack information of a catched exception to a new custom exception:
try
//here an unknown exception is rissen
except
on E: Exception do
begin
if ... then
raise EMyException.Create(E, CallStackOfExceptionEAsString);
end;
end;
What is the best way to do this, preferably using EurekaLog? I am using Delphi 2006 btw.
EurekaLog exposes several event handlers like OnExceptionNotify.
You can implement these in your code. For example: procedure EurekaLogExceptionNotify(
EurekaExceptionRecord: TEurekaExceptionRecord; var Handled: Boolean);
Here you can see a TEurekaExceptionRecord which is defined in ExceptionLog.pas. But you maybe just own the non-source version which works just fine.
The record has a EurekaExceptionRecord.CallStack list. This proprietary list can be converted to TStringsusing the CallStackToStrings method which is also defined in the ExceptionLog unit.
Here is an example where I write the CallStack into a StringList.
CallStackList := TStringList.Create;
try
CallStackToStrings(EurekaExceptionRecord.CallStack, CallStackList);
LogMessage := 'An unhandled exception occured. Here is the CallStack.' + #13#10
+ CallStackList.Text;
finally
CallStackList.Free;
end;
At least from this starting point you should be able to investigate the exposed functions, records etc.. All information is accessible.
EurekaLog provides a function GetLastExceptionCallStack() (defined in unit ExceptionLog.pas).
Using this I have written the following function (based on example code here):
function GetLastEurekalogCallStackAsString(): string;
{$IFDEF EUREKALOG}
var
Stack: TEurekaStackList;
Str: TStringList;
{$ENDIF}
begin
{$IFDEF EUREKALOG}
Stack := GetLastExceptionCallStack();
try
Str := TStringList.Create;
try
CallStackToStrings(Stack, Str);
Result := Str.Text;
finally
FreeAndNil(Str);
end;
finally
FreeAndNil(Stack);
end;
{$ELSE}
Result := '';
{$ENDIF}
end;
So you can write:
try
//here an unknown exception is rissen
except
on E: Exception do
begin
if ... then
raise EMyException.Create(E, GetLastEurekalogCallStackAsString());
end;
end;
EurekaLog 7 has Chained Exception support, which is specifically designed for this task. Just enable it in options (it is enabled by default) and use:
try
// here an unknown exception is rissen
except
on E: Exception do
begin
if ... then
Exception.RaiseOuterException(EMyException.Create(E.Message));
// for old IDEs:
// raise EMyException.Create(E.Message);
end;
end;

Resources