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;
Related
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;
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;
I need to capture all non-unit initialization exceptions in a Delphi 7 program so I can write the exception to a file, and perhaps show the user a message.
Reading up on this, I thought that a global exception handler would be cumbersome and that all I would need is to capture all exceptions at the DPR level. However, I can't get the code below to ever get to the ShowMessage in the dpr.
Why does the Raise Exception below actually result in an exception displayed on the screen rather than bouncing out to the .dpr's except clause? Maybe a global exception handler would be a better approach?
Shouldn't the code immediately below in the dpr capture all exceptions in a form?
In DPR:
begin
Application.Initialize;
try
Application.CreateForm(TForm1, Form1);
Application.Run;
except
On E: Exception do
ShowMessage('In dpr except. Exception is: ' + E.Message);
end;
end.
In Form:
Function TForm1.DoSomething( out aErrm: String):boolean; // force a failure for testing
begin
Result := FALSE;
aErrm := 'Failed in DoSomething';
end;
procedure TForm1.FormShow(Sender: TObject);
begin
try
fOk := DoSomething(fErrm);
except
fOk := FALSE;
Errm := 'Unexpected exception'
end;
if (NOT fOk) then
Raise Exception.Create(Errm) // why does this pop-up an exception when the DPR has an except around this code?
else
PostMessage(Handle, WM_CLOSE, 0, 0); // self-closing form
end; { FormActivate }
The main message loop in TApplication.Run wraps and handles all Messages in an exception block, which as a result catches all exceptions thus rendering your primary exception block in the DPR completely useless.
If you want to capture & handle Application exceptions then use TApplication.OnException.
We've created a Datasnap service (with Delphi XE), using Bob Swart's white paper as a guide. It works fine, and we've deployed it to our test server.
Now a problem occurs, when we have executed a large number of requests (through JMeter), some sort of memory corruption occurs. Some requests succeed, some fail with an access violation. In the end, it has become so corrupt, that every request to our OWN (not the DSAdmin) methods responds with an access violation.
However, I can't get my hands on a stacktrace to get more info, because the exception is already catched in the processing of the request.
If I test heavily with the VCL version of this application, it remains working correctly.
Has anyone any clue what this could be, or experienced the same problem, or can you help me get the stack trace from a caught exception (in someone else's code, which I can't edit)?
Thanks in advance.
To log both caught and uncaught exceptions using JEDI JCL, you should install the JEDI JCL.
Then try some code like this code taken from jcl\examples\windows\debug\framestrack\FramesTrackDemoMain.pas:
You should compile with full Debug information on in both the Compiler and Linker options in your delphi project options, for this to work.
Note that you don't have to call LogException, it's called automatically one you've added the exception notifier callback (JclAddExceptNotifier). don't forget to also call JclRemoveExceptNotifier, when the form or data module you are adding it from is destroyed, as shown here:
procedure TForm1.LogException(ExceptObj: TObject; ExceptAddr: Pointer; IsOS: Boolean);
var
TmpS: string;
ModInfo: TJclLocationInfo;
I: Integer;
ExceptionHandled: Boolean;
HandlerLocation: Pointer;
ExceptFrame: TJclExceptFrame;
begin
TmpS := 'Exception ' + ExceptObj.ClassName;
if ExceptObj is Exception then
TmpS := TmpS + ': ' + Exception(ExceptObj).Message;
if IsOS then
TmpS := TmpS + ' (OS Exception)';
mmLog.Lines.Add(TmpS);
ModInfo := GetLocationInfo(ExceptAddr);
mmLog.Lines.Add(Format(
' Exception occured at $%p (Module "%s", Procedure "%s", Unit "%s", Line %d)',
[ModInfo.Address,
ModInfo.UnitName,
ModInfo.ProcedureName,
ModInfo.SourceName,
ModInfo.LineNumber]));
if stExceptFrame in JclStackTrackingOptions then
begin
mmLog.Lines.Add(' Except frame-dump:');
I := 0;
ExceptionHandled := False;
while (chkShowAllFrames.Checked or not ExceptionHandled) and
(I < JclLastExceptFrameList.Count) do
begin
ExceptFrame := JclLastExceptFrameList.Items[I];
ExceptionHandled := ExceptFrame.HandlerInfo(ExceptObj, HandlerLocation);
if (ExceptFrame.FrameKind = efkFinally) or
(ExceptFrame.FrameKind = efkUnknown) or
not ExceptionHandled then
HandlerLocation := ExceptFrame.CodeLocation;
ModInfo := GetLocationInfo(HandlerLocation);
TmpS := Format(
' Frame at $%p (type: %s',
[ExceptFrame.ExcFrame,
GetEnumName(TypeInfo(TExceptFrameKind), Ord(ExceptFrame.FrameKind))]);
if ExceptionHandled then
TmpS := TmpS + ', handles exception)'
else
TmpS := TmpS + ')';
mmLog.Lines.Add(TmpS);
if ExceptionHandled then
mmLog.Lines.Add(Format(
' Handler at $%p',
[HandlerLocation]))
else
mmLog.Lines.Add(Format(
' Code at $%p',
[HandlerLocation]));
mmLog.Lines.Add(Format(
' Module "%s", Procedure "%s", Unit "%s", Line %d',
[ModInfo.UnitName,
ModInfo.ProcedureName,
ModInfo.SourceName,
ModInfo.LineNumber]));
Inc(I);
end;
end;
mmLog.Lines.Add('');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
JclAddExceptNotifier(Form1.LogException);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
JclRemoveExceptNotifier(Form1.LogException);
end;
This is the usual initialization code:
initialization
JclStackTrackingOptions := JclStackTrackingOptions + [stExceptFrame];
JclStartExceptionTracking;
Here's the JCL FramesTrackExample.dproj demo running:
For your purposes, change the code that adds a line to TMemo.Lines, to write to a log file on disk instead. If you already have a logging system, that's great, and if not, then consider Log4D.
Every new web service call is a new thread. Some resources can be allocated by previous thread when the next service call is coming and the new thread tries to access them. Or some resources may be released by one thread when another thread tries to use them. You should use TCriticalSection to make sure that all resources are available only for one thread. Also make sure that the TCriticalSection is a global variable and accessible by all instances.
Our application log the source line causing exception with JCL and it works great.
I use D2007. I have a TApplicationEvents.OnException event that do the actual logging.
Consider this:
function MyFunc: String;
begin
// Codelines that may raise exception.
// Call functions that also may raise exception
end;
procedure ComplexFunc(aVariable: String);
begin
// also here can it be exceptions....
// Code here that is the cause of exception
end;
procedure foo;
var
myVar: String;
begin
myvar := MyFunc;
ComplexFunc(myvar);
end;
procedure TMainForm.ApplicationEvents1Exception(Sender: TObject; E: Exception);
begin
LogLastException(E, 'Unhandled Exception (%s)', [E.Message], 20);
end;
I have 3 methods and my onException event.
LogLastException log the callstack when an exception occurs. The problem is that I cannot add information to the E.Message without loose the sourceline that cause exception. Pretend it is the second line in ComplexFunc that raise exception. I also want to log the value of myvar variable. So I change the code to:
function MyFunc: String;
begin
// Codelines that may raise exception.
// Call functions that also may raise exception
end;
procedure ComplexFunc(aVariable: String);
begin
// also here can it be exceptions....
// Code here that is the cause of exception
end;
procedure foo;
var
myVar: String;
begin
try
myvar := MyFunc;
ComplexFunc(myvar);
except
on E: Exception do
raise TException.CreateFmt('myvar = %s', [myvar]);
end;
end;
procedure TMainForm.ApplicationEvents1Exception(Sender: TObject; E: Exception);
begin
LogLastException(E, 'Unhandled Exception (%s)', [E.Message], 20);
end;
Now the value of myvar is logged, BUT at the price of I loose the original sourceline of the exception. Instead the line with raise TException.CreateFmt is logged. Any suggestion of how to do both ?
Regards
In addition to Marjan Vennema's answer (which I would follow), you can raise your new exception and make it look like it came from the address of the old exception.
except
on E: Exception do
raise Exception.CreateFmt('myvar = %s', [myvar]) at ExceptAddr;
end;
You are losing the original source line of the exception, because
except
on E: Exception do
raise TException.CreateFmt('myvar = %s', [myvar]);
end;
effectively handles the original exception (making it go away) and raising a new one. Which, of course then will have its own "source line of exception."
#balazs' solution preserves the source line of the original exception in the message of the new exception. #Stephane's solution comes close to the one I would use. Unfortunately he is replacing the original message with only the value of myvar. What I would do is add a line on top of the original message and then just re-raise the exception:
except
on E: Exception do
begin
E.Message := Format('%s'#13#10'%s', [Format('MyVar: %s', [MyVar]), E.Message]);
raise;
end;
end;
I've you tried something like that ?
try
myvar := MyFunc;
ComplexFunc(myvar);
except
on E: Exception do
begin
e.message := format('myvar = %s', [myvar]);
raise ;
end;
end;
I don't know what LogLastException do, but if you can redirect it's result into a string rather than your log, you can reraise the exception like this:
except
on E: Exception do
begin
str := LogLastExceptionToString(E, 'Unhandled Exception (%s)', [E.Message], 20);
raise TException.CreateFmt( str + 'myvar = %s message so far:' , [myvar]);
end;
end;
A simple approach would be to define a global variable, assign the extra information to it, and then add its contents to your log when you log the exception information.