Consider this part of code:
Try
arqTXT.LoadFromFile(LogPath);
finally
ShowMessage(" The log file could not be found. Check if the service is running. ");
end;
After my message, a system message appears with the message: Cannot open file "C:\log.txt". Is there a way to not show this system message?
I'm using Delphi 2010
Thanks.
You would like to trap that specific exception (EInOutError if I remember correctly) and let other exceptions that occurred to remain unmuted. So if you get some other exception (e.g. EOutOfMemory) if wont get "swallowed" leaving you with seemingly working but broken code.
try
..
except
on E: EInOutError do
begin
..
end;
end;
You can have many exceptions handlers in one construct. Any unhandled exceptions will continue to propagate outside of the try .. except block, until caught elsewhere (e.g. by default exception handler that shows the error box).
There's a couple of things to point out. As other answers/comments explain, you should be wrapping your code inside of a try..except block instead of a try..finally block. I'm not understanding how your code example is supposed to work, because as it's currently written, your message will always show, whether there was an exception or not. It should look something more like...
try
arqTXT.LoadFromFile(LogPath);
except
on E: EInOutError do begin
ShowMessage('The log file could not be found. Check if the service is running. Message: '
+ E.Message);
end
end;
That code will catch and handle I/O exceptions. You would use a different exception type instead of EInOutError to handle different types of exceptions, or the dirty way is to use on E: Exception to catch all types. You can also identify the specific error code using GetLastError and further recognize / log this error code to know exactly what's wrong (File missing, read-only, etc.), assuming that an I/O exception has occured.
Here's a good article explaining how to handle exceptions in Delphi.
The other thing to point out is when you see the exceptions. When you're in Debug mode (running application from the IDE), it will show all exceptions by default, even the ones that you don't see when your application runs on its own. You can disable this in the IDE. That article is for Delphi 2007, but I'm pretty sure it should apply for 2010 as well, because the same options are in Delphi XE2.
Solve it using
try
exception
on e: exception do
ShowMessage("My Message");
Instead of Try/Finally.
Related
I would like my custom error message to show up rather than the one generated by the system. Specifically, in this case, if the user tries to open a file that is already in use, I would like to catch the error and display my message, but the system always beats me to it and generates the following message: "Project...raised exception class EFOpenError with message 'Cannot open file "File path and name". The process cannot access the file because it is being used by another process'." When I close this error message, that is when my message is displayed. I would like only my message to be displayed. I do not want the system message displayed. Is there a way to do this? My code does not work.
Here is my code:
begin
//check if input file can be opened
try
if OpenDialog1.Execute then
begin
Application.ProcessMessages;
Memo1.Clear;
try
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
InputFile := TStringList.Create;
InputFile.LoadFromFile(OpenDialog1.FileName);
ActiveFileName := ExtractFileName(OpenDialog1.FileName);
lblActiveFileName.Caption := 'Active File: ' + ActiveFileName;
mmuDisplayClear.Enabled := True;
mmuAnalysisOptions.Enabled := True;
except on
EFOpenError do //if file does not exist or is in use
MessageDlgPos('File either does not exist or may be ' +
'in use!', mtError, [mbOK], 0, 300, 300);
end;
end;
finally
mmuDispInstructions.Enabled := True;
mmuDispData.Enabled := False;
end;
end;
You are seeing the debugger catch the exception before your application does. That is perfectly normal behavior. Simply press F9 to pass the exception to your app for normal handling. If you don't want the debugger to display the exception, put the exception class type in the debugger's Ignore list, or wrap the code in breakpoints that disable/enable the debugger's exception handling.
When you are running an application through Delphi IDE (debuging it) and an exceptions is raised from within try..except..end clause the exception will first be detected by IDE and if you then press F9 code from the except block will fire.
So if you generate custom message there that message will be shown. But you don't need to show a message. You might wanna handle that exception silently.
Let's take a look at the next example: You are trying to establish a network connection to some server. In the call for establishing connection you specify certain timeout after which atempt to connect is considered as failed. After this timeout expires most network components raise a Etimeout exception so you know that establishing connection wa unsucsessfull. But due to the way how networks sometimes behave you might wanna go and retry the atempt to connect to the server again before showing an error to the user. You can do this by simply calling connect method again within the except block in order to initiate another atempt wihout rasing any errors. So code would look someting like this:
Timeout := 2000;
try
NetworkComponent.Connect(Timout);
except
try
NetworkComponent.Connect(Timeout);
except
MessageDlgPos('Connection could not be established!,
mtError, [mbOK], 0, 300, 300);
end;
end;
As you see in the example in first try..except..end block we handle the exception silently but in the next try..except..end block which is nested within the except block of the first try..except..end; block where we finally show message to the user.
While this is generaly considered as bad practice sometimes you might not even want to show any message abot such error. For instance if you are making some system service or a background process one thing that you definitly woul not want is to bother user with bunch of error messages. it is still good to athleast write the errors in some log for further debuging.
Now if you application is ran outside the Delphi IDE (not debuging) or if you turn of specific Exception detection in IDE then the code within the except block will fire directly. when specific exception is raised.
EDIT: Removed unrelated text.
I have an application which occasionally returns an integer overflow when FormatDateTime is called. I have no idea what scenario triggers this, although I have found mention of the problem here and here.
My current workaround is to wrap the function and handle the exception:
function FormatDateTimeEx (const FormatString : ANSIString ;
DateTime : TDateTime) : ANSIString ;
begin
try
Result := FormatDateTime (FormatString, DateTime) ;
except
Result := '???' ;
end ;
end ;
which solves the issue of this problem causing the compiled executable to crash, but I would also prefer it if the debugger did not break on the EIntOverflow exception when I an running the program in the IDE. I don't want to do this by telling the IDE to ignore the EIntOverflow exception, because then it won't break on other occasions when an untrapped integer overflow occurs (right?).
Is there a programmatic way (compile-time or run-time) of telling the IDE to locally ignore an exception class, for those occasions when an exception can occur, and you already know that and are handling it?
Is there a programmatic way (compile-time or run-time) of telling the IDE to locally ignore an exception class, for those occasions when an exception can occur, and you already know that and are handling it?
No.
There's no way for you to ask the debugger to ignore certain EIntOverflow exceptions but not others. You either ignore them all, or none of them. The only way you can control which exceptions break in the debugger is through the exception class. You cannot ask the debugger to ignore exceptions in one part of the program, but not another.
Frankly, it seems to me that the right way to tackle this is to work out why you are triggering these exceptions in the first place. And do something to prevent them from occurring at all. The exceptions are symptomatic of a bug in your code which you need to tackle head on.
You can't control debugger exception handling but you can control compiler options which lead to some of these exceptions.
Here's how you can locally disable compiler switch and then return to it previous state:
1) Initialization - somewhere in the start of a unit or maybe in separate *.inc file.
{$IFOPT R+}
{$DEFINE RangeCheckOn}
{$ENDIF}
Here you save the initial (i.e. being set in project settings) state of a compiler switch.
2) Now, the usage:
{$R-}
...your code here with disabled switch...
{$IFDEF RangeCheckOn}
{$R+}
{$ENDIF}
Here you disable the switch, execute some code without it and return it to initial state when you're done.
I'm using this trick for doing some bit shift operations over (U)Int64 which raise wrong EIntOverflow exception when touching the MSB (compiler bug that is actual for XE2, not sure for newer versions).
But I agree with previous answers that you should check the real reason of the exception being raised. I'd advice you to use your FormatEx version where parameter values would be logged in case of exception. Thus you may track the values that cause exception and check if it's a reproducible case.
Is there a programmatic way (compile-time or run-time) of telling the IDE to locally ignore an exception class, for those occasions when an exception can occur, and you already know that and are handling it?
Not in code, no. However, it can be done in the debugger using Breakpoints.
Wrap the affected code with two Breakpoints:
function FormatDateTimeEx(const FormatString: AnsiString; DateTime: TDateTime): AnsiString;
begin // <-- breakpoint
try
Result := FormatDateTime (FormatString, DateTime) ;
except
Result := '???';
end;
end; // <-- breakpoint
Go into the Properties of the first Breakpoint and enable the Ignore subsequent exceptions option and disable the Break option.
Go into the Properties of the second Breakpoint and enable the Handle subsequent exceptions option and disable the Break option.
I use a function to log application errors to a file.I have it on my main form.How do I tell my other forms, in case of an error,to use this function? Just by chance I added this to my query on some other form:
......
try
ClientDataSet1.Execute;
ClientDataSet1.Close;
except
on E:Exception do begin
ShowMessage('Error : ' + E.Message);
LogError(E);
....
My LogError(E); is not recognized (gives error). Tried adding the function :procedure LogError(E:Exception); to public uses but wont work.
Structured Exception Handling
I'd like to point out that your approach to exception handling is very tedious and wasteful coding. Not to mention quite dangerous. You don't want to be littering your code with individual handlers to log your exceptions when there's a much more practical approach.
Fortunately Delphi provides the means to make this much easier. First a little background about structured exception handling... Most developers make the mistake of trying to deal with errors by writing code like the following:
try
//Do Something
except
//Show Error
//Log Error
end;
The problem with the above code is that it swallows the error. Even though the user sees an error message, the error itself is hidden from the rest of the program. This can result in other parts of the program doing things they should not do because of the error.
Imagine a program that calls the following routines: LoadAccount; ApplyDiscount; ProcessPayment;. But the the first routine raises an error. Which a programmer (mistakenly thinking they're being diligent) decided to "handle" as above. The problem is that the next two routines will still be called as if nothing is wrong! This could mean that a discount is applied to and payment processed for the wrong account!
The point is that structured exception handling saves us from these headaches (provided we don't break the way it works). If the LoadAccount routine didn't try "handle" the exception, then while the application is in an "exception state" the code would simply keep jumping out of routines until it finds a finally / except handler.
This suits an event driven program very nicely.
Suppose a user clicks a button that will cause your program to start a multi-step task: with loops, calls to child methods, creating objects etc. If anything goes wrong, you want to abandon the entire task and wait for the next input. You don't want to stubbornly keep trying to complete the task; because you'll either just get more errors, or worse: later changes will do the wrong thing because earlier required steps did not complete successfully.
Easy Logging of Errors
As mentioned earlier, if you simply leave an exception alone, the error will "bubble up" to an outer exception handler. And in a standard GUI application, that will be the default Application exception handler.
In fact at this stage, doing nothing special: if an exception is raised in the middle of the button click task described earlier, the default exception handler will show an error message to the user. The only thing missing is the logging. Fortunately, Delphi makes it easy for you to intercept the default Application exception handler. So you can provide your own implementation, and log the error as you desire.
All you need to do is:
Write a method using the TExceptionEvent signature.
E.g. procedure MyHandleException(ASender: TObject; E: Exception);.
Then assign your custom handler using: Application.OnException := MyHandleException;
General Logging
Of course, this only covers logging of exceptions. If you want to do any other ad-hoc logging, you want to be able to reuse the code. This basically requires you to move your logging code into a separate unit that all your other units can call as needed.
So putting these things together you might do something as follows:
TLogger = class
public
procedure WriteToLog(S: string); //Performs actual logging of given string
procedure HandleException(ASender: TObject; E: Exception); //Calls WriteToLog
end;
Then you might set it up in your program unit as follows:
begin
Logger := TLogger.Create(...);
Application.OnException := Logger.HandleException;
Logger.WriteToLog('Custom exception handler has been assigned.');
//Rest of application startup code
end;
Final Thoughts
I mentioned that if an unexpected exception occurs, you want to abandon the current task. But what happens if you've already done some things that should now be undone as a result of the error. Then you would simply implement it as follows:
try
//Do something
except
//Undo
raise; //NOTE: more often than not, one should re-raise an
//exception so that callers are aware of the error.
end;
Note that this is the primary use for exception handlers. Yes they can also be used to swallow exceptions. But they should only swallow an exception if it has been truly resolved.
While Delphi is object-oriented language, it can deal with non-OO entities as well.
Think, your main form is property of which form ?
The answer is that main form is not the property of ANY other form, it is a global object. Same exactly thing should be done about your function.
unit Main;
interface uses .......;
type TMainForm = class (TForm)....
....
end;
Procedure LogError(const message: string);
implementation uses .....;
Procedure LogError(const message: string);
begin
...
end;
(**********)
procedure TMainForm.FormCreate(...);
begin
....
end;
...
end.
Now even better option would be to totally decouple LogError from ANY form at all.
Unit MyLogger;
interface uses ....; // but does not uses Main or any other form
procedure SetupLog(....);
// to initialize it and change any settings you may wish
// maybe you would choose to save messages to file
// or use Windows debug interface - OutputDebugString
// or to a network SysLog server
// or just WriteLn it to a text window
// or to some TMemo in a graphic window
// or to file AND to a memo - that also might make sense.
// just keep it extensible
Procedure LogWarning(const message: string);
Procedure LogError(const message: string);
implementation uses ...;
function GenericLoggingSink(.....);
begin
...
end;
Procedure LogError(const message: string);
begin
GenericLoggingSink( Log_Message_Type_Error, message, ... );
end;
Procedure LogWarning(const message: string);
begin
GenericLoggingSink( Log_Message_Type_Warning, message, ... );
end;
And your Main form should just use this unit and this function on the same terms as all other forms in your application.
As part three I suggest you think what you want from your logging procedures.
Actually doing this only makes sense for very simplistic logging needs. Very simplistic.
Otherwise just take any existing Delphi logging frameworks and utilize a lot of functionality implemented there. For example can you just log an object of any class you would write?
The only reason to make your own logging library is "my program is so simplistic, that it does need only the most primitive logging. It would be faster to improvise my own system, than to copy-paste library initialization and setup from examples to a ready-made libraries".
Log4Delphi and Log4D are well-known FLOSS libraries, though they look somewhat abandoned. Maybe there just is nothing left to be extended. Those are most old ones, but there also are some newer FLOSS libraries, an easy example being http://blog.synopse.info/post/2013/02/03/Log-to-the-console
If anything, you can read their texts and learn from them.
There are also commercial libraries like those listed at Is there a way to do normal logging with EureakLog? and Delphi itself is shipped with a limited version of CodeSite logging framework.
Delphi IDE enhancements like CnWizards and GExperts also do come with a simplified debug-oriented logging interface.
Google would bring you even more options if you'd dare :-)
Generally I make my exception handlers handle only very specific exceptions, so that the code doesn't try to recover from exceptions that weren't anticipated and potentially aren't recoverable.
But there are a few places where it is impossible to know ahead of time what exceptions could legitimately come up from which recovery is perfectly possible (because of calls to libraries that don't specify what exceptions they might raise). So in these special cases I do in fact trap all exceptions: on e:Exception ....
But this also handles EAssertionFailed exceptions, which really should never be handled because they imply incorrect code.
So I've started writing exception handlers like this:
on e:Exception do
begin
if e is EAssertionFailed then
begin
raise;
end;
…
This just seems ugly and error-prone. (What if I forget the if e is EAssertionFailed code?)
Is there a better way? (Should I just never ever use on e:Exception …, except in a top level handler that aborts the program?)
Update
I think David's comment below is correct - never use a blanket exception handler (other than to log the exception and stop the program).
The problem then becomes "How can I get a list of all exception types that are safe to handle arising from some arbitrary bit of code?" I don't think this is one that Stack Overflow can answer.
(The particular problem is a 3rd party library that reads a configuration file. There are fairly obvious exceptions for filing system errors, but the library seems to raise numerous and almost random exception types for syntax errors within the file itself. I don't think I could ever be sure that I've exhausted the possibilities. I certainly cannot have my application fall over because the configuration file couldn't be read!)
The standard Delphi exception handling pattern is
try
// Some code which may raise an exception
except
on E: SomeError do begin
// Handle SomeError and derived classes
end;
on E: SomeOtherError do begin
// Handle SomeOtherError and derived classes
end;
...
end;
it does not require the use of on E:Exception ….
If you don't know the exception type you should not handle it; or sometimes (ex in DLL code) you should handle ALL exceptions.
Why don't to use this:
try
...
except
on EAssertionFailed do raise;
on E: .... do ... ; // your exception
on E: .... do ... ; // your exception
on E: .... do ... ; // your exception
on E: Exception do ... ; // all other exceptions
end;
If any of the handlers in the exception block matches the exception, control passes to the first such handler. An exception handler 'matches' an exception just in case the type in the handler is the class of the exception or an ancestor of that class.
Or you can remove on EAssertionFailed do raise; and on E: Exception do ... ; from the exception block and then exception handler will be searched in the next-most-recently entered try...except statement that has not yet exited.
I'm offering my own answer here to see what the community thinks.
The point wasn't a misunderstanding of how exception handling works in Delphi. I've summarised the particular problem in my update to the question.
My solution is going to be to convert the many different exception types the library can raise into a single type that I can handle explicitly. I'll put a blanket exception handler around the one problem call to the library. This handler will raise a new exception of a known type, to be caught explicitly higher up at the appropriate time.
(And no, the exceptions raised by the library call do not have any common base type other than Exception itself, so I really do have to catch all exceptions if I want to recover from a failure in the library call.)
Crucially, I believe that although I cannot hope to enumerate all the exceptions that this library call could raise, I can be sure that it won't put the application into an unstable state when it raises an exception.
Update
In the end I was able to use something like this:
on e:Exception do
begin
if (e.ClassType = Exception) or (e is EConvertError) or
(e is EVariantError) then
begin
ShowMessage('The application settings could not be loaded.');
end
else
begin
// An exception we didn't anticipate. Assume it is fatal.
raise;
end;
end;
A lot of the recoverable exceptions raised by the library are of type Exception, but note that only Exception and the specified descendants are handled. The others are raised indirectly by the runtime library. There might turn out to be other exceptions that should be handled too, but this seems like a good start. It's better to have the application bail out when it needn't have done so than to continue running and corrupt the user's data.
I want to disable the exception catching by Delphi and let Windows catch it - making it produce a window like "AppName crashed. Debug , Send", add this to Application events, create a memory dump and so on.
By default, Delphi catches all the exception in TApplication.Run procedure... How can I avoid that without modifying Forms.pas?
You could add an OnException handler that re-raised the exception:
class procedure TMainForm.OnException(Sender: TObject; E: Exception);
begin
raise Exception(AcquireExceptionObject);
end;
initialization
Application.OnException := TMainForm.OnException;
I'm not sure why you would want to do this at all though. It's more normal to use a tool like madExcept or EurekaLog to show an error dialog that yields much more helpful information than the system dialog.
You can set JITEnable to '1' or higher (default is '0'). With '1', non native exceptions, with higher than '1', all exceptions will be handled by JIT or WER (depending on the system).
This may not be what you want though. With this solution any qualifying exception will be passed to the OS, it doesn't matter if they're handled in code or not. Clarification (run outside the debugger):
procedure TForm1.Button1Click(Sender: TObject);
begin
raise EAccessViolation.Create('access denied');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
try
PInteger(0)^ := 0;
except
end;
end;
initialization
JITEnable := 1;
The first example is a native exception, it will be handled by the application exception handling mechanism when JITEnable is 1. But the second example will trigger JIT/WER.
Add your own handler. Application.OnException is probably what you want. Better than leaving it up to windows as well, as you get different behaviours depending on the environment. For instance if windows error reporting is on, it will ask the user if they want to send an error report to MS.
Like Mr Heffernan I recommend you look at something like EurekaLog.
AS. I agree with voices above that this wish is rather strange.
I also agree that practically hooking in TApplication.OnException would probably be enough ("if it looks like a duck...")
However if you truly want to make RTL oblivious to exceptions, there are ways too.
Exception handlers are plugin to low-level RTL, just like heap management, etc.
You can look at KOL (Key Objects Library).
In Delphi 5 times i managed to make 2KB-size DLL.
That required absense of many usualyl taken "for granted" features. Exception were among them.
To enable Exceptions in KOL's system RTL replacement, you had to make some $DEFINE's, and then the code to add exceptions support to IDE was unlocked.
I believe you can still get that modularized RTL version and grep for that $IfDef and see which code is replaced with which.
I believe there is fair chance you can undo that and make Windows avoid calling Delphi RTL over Exceptions.
I don't remember details, but i believe Delphi RTL Exception handler is just registered in Windows core as a callback. And you probably can de-register it (register nil callback).
I believe you can find it in stock RTL, but KOL's modularised RTL would just make it easier to search.