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;
Related
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.
Who is responsible for error checking and handling?
I don't have any of the expensive component libraries such as DevExpress or TMS Components etc so I cannot look at source to get an idea of how most components manage error handling.
Specifically what I am wanting to know is should there be a limit to how many errors and warnings component developers should try to capture? Is there a balance between having meaningful error checking and just making it too easy for developers using your component?
Here is an example using a few scenarios:
Note these are directly from the components source (made up for example purposes)
procedure TMyComponent.AddFromFile(FileName: string);
begin
FBitmap.LoadFromFile(FileName);
end;
or
procedure TMyComponent.AddFromFile(FileName: string);
begin
if FileExists(FileName) then
begin
FBitmap.LoadFromFile(FileName);
end
else
raise Exception.Create(FileName + ' does not exist.');
end;
And these last two are using an instance of the component at runtime:
procedure TForm1.FormCreate(Sender: TObject);
begin
MyComponent1.AddFromFile('D:\Test.bmp');
end;
or
procedure TForm1.FormCreate(Sender: TObject);
begin
if FileExists('D:\Test.bmp') then
begin
MyComponent1.AddFromFile('D:\Test.bmp');
end
else
raise Exception.Create('D:\Test.bmp does not exist.');
end;
I guess it comes down to who should error check and handle what? Is the component developer responsible for handling these types of checking or the user of the component?
As I am writing this I believe both component developer and user should handle such checking but I am unsure, so I am looking for what the general consensus amongst developers is.
Thanks.
To answer your specific queestion:
Specifically what I am wanting to know is should there be a limit to how many errors and warnings component developers should try to capture? Is there a balance between having meaningful error checking and just making it too easy for developers using your component?
The general rule about exception handling is that you should only catch exceptions you know how to handle, and let others propagate to higher code that may know how to handle it. If an exception is raised inside of your component, the component needs to decide whether to:
handle that particular exception internally and gracefully move on to other things without notifying the caller at all.
re-throw the exception (maybe with tweaks made to it), or re-throw a whole new exception, to allow the caller to identify and handle that specific failure, if desired.
ignore the exception (don't catch it at all) and just let it propagate as-is.
If an API used by your component returns an error code instead of raising an exception, the component needs to decide how to handle that as well. Whether to ignore the error and move on, or raise an exception to make it more apparent.
In your particular example, I prefer the following approach:
type
EMyComponentAddError = class(Exception)
private
FFileName: String;
begin
constructor CreateWithFileName(const AFileName: string);
property FileName: string read FFileName;
end;
constructor EMyComponentAddError.CreateWithFileName(const AFileName: string);
begin
inherited CreateFmt('Unable to add file: %s', [AFileName]);
FFileName := AFileName;
end;
procedure TMyComponent.AddFromFile(FileName: string);
begin
try
FBitmap.LoadFromFile(FileName);
except
Exception.RaiseOuterException(EMyComponentAddError.CreateWithFileName(FileName));
end;
end;
This allows your component to recognize that an error occurred, act on it as needed, and still report component-specific information to the caller without losing the original error that caused the actual failure. If the caller is interested in the details, it can catch the exception, look at its InnerException property, access custom properties if present, etc.
For example:
procedure TForm1.FormCreate(Sender: TObject);
begin
MyComponent1.AddFromFile('D:\Test.bmp');
end;
Let's assume MyComponent1.AddFromFile('D:\Test.bmp'); fails. The default exception handler will catch it and display a popup message that says:
Unable to add file: D:\Test.bmp
Useful, but little details, as it could have failed for any number of reasons. Maybe the file could not be opened, but why? Non-existant vs no permission? Maybe the file was opened but corrupted? Maybe memory could not be allocated? And so on.
The caller could catch it and display more useful info, if so desired (it is not required - the component provides the info, the caller decides whether to use it or not):
procedure TForm1.FormCreate(Sender: TObject);
begin
try
MyComponent1.AddFromFile('D:\Test.bmp');
except
on E: EMyComponentAddError do
begin
ShowMessage('There was a problem adding a file:'+sLineBreak+E.FileName+sLineBreak+sLineBreak+E.InnerException.Message);
Sysutils.Abort;
end;
end;
end;
Or:
procedure TForm1.FormCreate(Sender: TObject);
begin
try
MyComponent1.AddFromFile('D:\Test.bmp');
except
on E: EMyComponentAddError do
begin
raise Exception.CreateFmt('There was a problem adding a file:'#10'%s'#10#10'%s', [E.FileName, E.InnerException.Message]);
end;
end;
end;
Either of which would display:
There was a problem adding a file:
D:\Test.bmp
The file was not found
As David said we only need this
procedure TMyComponent.AddFromFile(FileName: string);
begin
FBitmap.LoadFromFile(FileName);
end;
This will check that
there is an existing file
in this file is a valid bitmap
Now it depends on the application, how important is this for the application. If this TForm1 is the Application.MainForm, every exception you did not catch inside the creation process will terminate the application. This is sometimes a valid behavior.
Very important, the application cannot run without
procedure TForm1.Form1Create(Sender:TObject);
begin
MyComponent.AddFromFile( 'D:\Test.bmp' );
end;
or wrap the exception for a user-friendly message
procedure TForm1.Form1Create(Sender:TObject);
begin
try
MyComponent.AddFromFile( 'D:\Test.bmp' );
except
on E: Exception do
raise Exception.Create( 'Sorry, I cannot run, because of: ' + E.Message );
end;
end;
Very important, but we have a fallback to handle this, maybe
procedure TForm1.Form1Create(Sender:TObject);
var
LBitmapFiles : TStringList;
LBitmapIdx : Integer;
LBitmapLoaded : Boolean;
LErrorStore : TStringList;
begin
LBitmapFiles := nil;
LErrorStore := nil;
try
LBitmapFiles := TStringList.Create;
LErrorStore := TStringList.Create;
LBitmapFiles.Add( 'D:\Test.bmp' );
LBitmapFiles.Add( 'D:\Fallback.bmp' );
LBitmapLoaded := False;
while not LBitmapLoaded and ( LBitmapIdx < LBitmapFiles.Count ) do
try
MyComponent.AddFromFile( LBitmapFiles[LBitmapIdx] );
LBitmapLoaded := True;
except
on E: Exception do
begin
LErrorStore.Add( LBitmapFiles[LBitmapIdx] + ': ' + E.Message );
Inc( LBitmapIdx );
end;
end;
if not LBitmapLoaded then
raise Exception.Create( 'Sorry, I cannot run, because of: ' + LErrorStore.Text );
finally
LErrorStore.Free;
LBitmapFiles.Free;
end;
end;
There are other fallbacks possible and this also depends on the application (f.i. set a dummy bitmap to the component) to get the application to work properly.
Not important, if we have no image ... we have no image, who cares
procedure TForm1.Form1Create(Sender:TObject);
const
CBitmapFile = 'D:\Test.bmp';
begin
// check, if there is a file
if FileExists( CBitmapFile ) then
try
MyComponent.AddFromFile( CBitmapFile );
except
on E: Exception do
begin
// Maybe log the exception
SomeLogger.Log( E );
// Maybe set some extra parameters for the application to know, this has failed
RunningWithoutBitmap();
end;
end
else
// Maybe set some extra parameters for the application to know, this has failed
RunningWithoutBitmap();
end;
Component
procedure TMyComponent.AddFromFile(FileName: string);
begin
FBitmap.LoadFromFile(FileName);
end;
This is all you need. If the bitmap object cannot load the file, for whatever reason, it will raise an exception. Let that exception propagate to the consumer of the code.
There's really no point trying to test whether or not the file exists. What if the file exists and it is not a bitmap file? What if the file exists, is a bitmap file, but the disk has a duff sector and the file read fails? If you attempt to check for all error conditions, you will just be repeating the checks that the LoadFromFile method already does.
Some error conditions cannot possibly be checked from the outside. An error that only becomes apparent part way through reading the file cannot reasonably be checked from the outside.
One very common consequence of over-zealous, duplicate error checking is that you end up with code that produces errors in scenarios where there should be none. If you get your error checking wrong you could end up reporting an error that would not have occurred had you let the underlying code run.
Consumer
procedure TForm1.FormCreate(Sender: TObject);
begin
MyComponent1.AddFromFile('D:\Test.bmp');
end;
At this point the decision is more difficult. I would typically expect the following question to be the driver of the decision:
Is it an expected, and reasonable event, for the file not to be present?
If the answer to that question is yes, then you should consider handling the exception in the FormCreate method. Again, testing FileExists() catches just one failure mode, albeit a common one. Perhaps you should use a try/except block to catch the error.
If the answer to the question is no, let the error propagate.
That said, you should also consider whether or not you want an exception to be thrown from your form's OnCreate event handler. That may be perfectly reasonable, but it is certainly conceivable that you will not wish to do this.
I'm using Delphi 7 and in an attempt to handle all the possible exceptions being thrown during the run of the program. I used Application.OnException := HandlerProcedure; to handle exceptions but when exception occurs, HandlerProcedure never gets called. In order to assure if it really works, I raised exception after I assigned Application.OnException as below:
Application.OnException := HandlerProcedure;
raise Exception.Create('Exception');
and defined HandlerProcedure as:
procedure TFormMain.HandlerProcedure(Sender: TObject; E: Exception);
begin
ShowMessage('Exception.');
Exit;
end;
But HandlerProcedure never gets called. How can I make it handle all the exceptions?
If you want to intercept ALL exceptions, you need to implement a RTLUnwindProc low-level procedure.
This is a bit low-level (e.g. it needs asm skills), so you should better rely on existing code. See this stack overflow question. I even put some reference code (including low-level asm, working with Delphi 7 and later under Win32) in my own answer.
Something is wrong in your code. The example from Embarcadero's website is working perfect.
{
In addition to displaying the exception message, which
happens by default, the following code shuts down the
application when an exception is not caught and handled.
AppException should be declared a method of TForm1.
}
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := AppException;
end;
procedure TForm1.AppException(Sender: TObject; E: Exception);
begin
Application.ShowException(E);
Application.Terminate;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
raise EPasswordInvalid.Create('Incorrect password entered');
end;
Also good practices on handling errors on Delphi are described here.
In order to further investigate the problem you have, you should take a look at this https://stackoverflow.com/questions/1259563/good-os-delphi-exception-handling-libraries
If you are using a third party exception handler such as madExcept, Application.OnException no longer fires. You must instead code TMadExceptionHandler.OnException event or directly call RegisterExceptionHandler.
I have created a procedure in a dll that opens a form and then prints a report.
This procedure works perfectly from an exe.
I have wrapped the unit that contains this procedure and forms in a dll and exported the procedure as follows:
{$R *.res}
Procedure PrintTopSellers; stdcall;
begin
Form1 := TForm1.create(nil);
GetMonth := TGetMonth.create(nil);
Form1.PrintTopSellers;
end;
exports PrintTopSellers;
begin
end.
Now I call this procedure PrintTopSellers from an exe as follows:
procedure TForm1.Button5Click(Sender: TObject);
type
TRead_iButton = function :integer;
var
DLL_Handle: THandle;
Read_iButton: TRead_iButton;
Begin
DLL_Handle := LoadLibrary('c:\Catalog.dll');
if DLL_Handle <> 0 then
begin
#Read_iButton:= GetProcAddress(DLL_Handle, 'PrintTopSellers');
Read_iButton;
end;
application.ProcessMessages;
FreeLibrary(DLL_Handle);
end;
The call to the procedure works perfectly. However, after I close the calling exe, I get an access violation - "Access violation at address 00BAC89C. Read of address 00BAC89C."
Appreciate any assistance. I am using Delphi 7.
Thanks
You are creating Form1, a windowed control, in the DLL. But you never destroy it. Then you unload the DLL which unloads the code that implements the window procedures for all windows created by the DLL. Presumably when the process shuts down, the window procedures are called, but there is no code there anymore.
Fix the problem by destroying all objects that the DLL creates. It looks to me like the best approach is to do that when PrintTopSellers terminates.
Procedure PrintTopSellers; stdcall;
begin
Form1 := TForm1.create(nil);
try
GetMonth := TGetMonth.create(nil);
try
Form1.PrintTopSellers;
finally
GetMonth.Free;
end;
finally
Form1.Free;
end;
end;
In the code that loads the DLL, TRead_iButton is declared incorrectly. It should be
TRead_iButton = procedure; stdcall;
But that doesn't actually explain the problem here since the signature mismatch is benign for a parameterless procedure.
"TRead_iButton = function: integer; register;"
"Procedure PrintTopSellers; stdcall;"
Absolutely different conventions/types, ain't them ?
Make them the same.
And better ditch DLL and use packages (BPL), then compiler would make you safe from such errors
We also don't see the code neither in Form1.PrintTopSellers nor in TGetMonth. The all can leave some dangling pointers in the host exe, that would get accesses after DLL unloaded.
Show exactly chain of function calls leading to AV - it is called stack trace.
Debug info + some excaption interrupt like Jedi CodeLibrary (used by Delphi IDE) madExcept, EurekaLog, synopse log and a lot of other exist.
Display the call stack in a Delphi Win32 application
Does DLL or EXE use Runtime packages ?
While browsing System.Zip (Delphi XE2) to see how it works, I found this function:
procedure VerifyWrite(Stream: TStream; var Buffer; Count: Integer);
begin
if Stream.Write(Buffer, Count) <> Count then
raise EZipException.CreateRes(#SZipErrorWrite) at ReturnAddress;
end;
It's the at ReturnAddress part that sort of puzzles me.
I didn't know that at was a valid keyword (the syntax highlighter doesn't seem to recognise it either).
According to the IDE it's declared as System.ReturnAddress, but I can only find it declared as a label somewhere in the (asm) code of procedure _HandleAnyException;. The system unit is full of references to it though.
So what I would like to know is this:
What is ReturnAddress?
What exactly does Raise Exception.Create ... at ReturnAddress do?
Bonuspoints if you can give a real-world example of where this would be a useful construct, or if you can advice against using it.
ReturnAddress is the address to which VerifyWrite would have returned when finished.
Raise Exception.Create... at ReturnAddress means that when the exception dialog is displayed, it would indicate the address of the exception as being at ReturnAddress. In other words, the exception message would read Exception <whatever> raised at <ReturnAddress>: <Exception Message>.
Here is an excerpt from the help file for Delphi 7. It's nearly the same as the online version.
To raise an exception object, use an instance of the exception
class with a raise statement. For example,
raise EMathError.Create;
In general, the form of a raise statement is
raise object at address
where object and at address are both optional; see
Re-raising exceptions. When an address is specified,
it can be any expression that evaluates to a pointer
type, but is usually a pointer to a procedure or function.
For example:
raise Exception.Create('Missing parameter') at #MyFunction;
Use this option to raise the exception from an earlier point
in the stack than the one where the error actually occurred.
Note the last sentence in particular. It's pretty specific about the use of at <address>.
ReturnAddr was not a puzzle with previous Delphi versions. Consider next test (Delphi XE):
procedure RaiseTest1;
procedure RaiseException(ReturnAddr: Pointer);
begin
raise Exception.Create('OOPS!') at ReturnAddr;
end;
asm
POP EAX
JMP RaiseException
end;
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
RaiseTest2;
end;
if you press Button3 under debugger and press 'Break' in exception messagebox, debugger stops at
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1; // <-- here
end;
if you press Button4, debugger stops at
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!'); // <-- here
end;
As you can see RaiseTest1 modifies default exception stack frame and makes debugging a bit more straightforward since the only purpose of RaiseTest1(2) procedures is to raise an exception.
I guess something changed in XE2 so that ReturnAddr syntax is simplified.