How to avoid exceptions when using TGeckoBrowser in a Delphi app - delphi

Prompted by a q here yesterday, I'm trying to re-familiarise myself with TGeckoBrowser
from here: http://sourceforge.net/p/d-gecko/wiki/Home.
(Nb: requires the Mozilla XulRunner package to be installed)
Things seem to have moved backwards a bit since I last tried in the WinXP era, in that
with a minimal D7 project to navigate to a URL, I'm getting errors that I don't recall
seeing before. I've included my code below. These are the errors which I've run into
navigating to sites like www.google.com, news.bbc.co.uk, and here, of course.
The first exception - "Exception in Safecall method" - occurs as my form first displays, before naviagting anywhere at all. I have a work-around in the form of a TApplication.OnException handler.
My q is: a) Does anyone know how to avoid it in the first place or b) is there a tidier way of catching it than setting up a TApplication.Exception handler, which always feels to me like a bit of
an admission of defeat (I mean having one to avoid the user seeing an exception, not having an application-wide handler at all).
This exception occurs in this code:
procedure TCustomGeckoBrowser.Paint;
var
rc: TRect;
baseWin: nsIBaseWindow;
begin
if csDesigning in ComponentState then
begin
rc := ClientRect;
Canvas.FillRect(rc);
end else
begin
baseWin := FWebBrowser as nsIBaseWindow;
baseWin.Repaint(True);
end;
inherited;
end;
in the call to baseWin.Repaint, so presumably it's
presumably coming from the other side of the interface. I only get it the first
time .Paint is called. I noticed that at that point, the baseWin returns False for GetVisibility,
hence the experimental code in my TForm1.Loaded, to see if that would avoid it.
It does not.
2.a After calling GeckoBrowser1.LoadURI, I get "Invalid floating point operation"
once or more depending on the URL being loaded.
2.b Again, depending on the URL, I get: "Access violation at address 556318B3 in module js3250.dll. Read of address 00000008." or similar. On some pages it occurs every few seconds (thanks I imagine to some JS timer code in the page).
2a & 2b are avoided by the call to Set8087CW in TForm1.OnCreate below but I'm
mentioning them mainly in case anyone recognises them and 1 together as symptomatic
of a systemic problem of some sort, but also so google will find this q
for others who run into those symptoms.
Reverting to my q 1b), the "Exception in Safecall method" occurs from StdWndProc->
TWinControl.MainWndProc->[...]->TCustomGeckoBrowser.Paint. Instead of using an
TApplication.OnException handler, is there a way of catching the exception further
up the call-chain, so as to avoid modifying the code of TCustomGeckoBrowser.Paint by
putting a handler in there?
Update: A comment drew my attention to this documentation relating to SafeCall:
ESafecallException is raised when the safecall error handler has not been set up and a safecall routine returns a non-0 HResult, or if the safecall error handler does not raise an exception. If this exception occurs, the Comobj unit is probably missing from the application's uses list (Delphi) or not included in the project source file (C++). You may want to consider removing the safecall calling convention from the routine that gave rise to the exception.
The GeckoBrowser source comes with a unit, BrowserSupports, which looks like a type library import unit, except that it seems to have been manually prepared. It contains an interface which includes the Repaint method which is producing the SafeCall exception.
nsIBaseWindow = interface(nsISupports)
['{046bc8a0-8015-11d3-af70-00a024ffc08c}']
procedure InitWindow(parentNativeWindow: nativeWindow; parentWidget: nsIWidget; x: PRInt32; y: PRInt32; cx: PRInt32; cy: PRInt32); safecall;
procedure Create(); safecall;
procedure Destroy(); safecall;
[...]
procedure Repaint(force: PRBool); safecall;
[...]
end;
Following the suggestion in the quoyed documentation, I changed th "safecall" to StdCall on the Repaint member (but only that member) and, presto!, the exception stopped occurring. If it doesn't reappear in the next couple of days, I'll post that as an answer, unless anyone comes up with a better one.
My project code:
uses
BrowserSupports;
procedure TForm1.FormCreate(Sender: TObject);
begin
Set8087CW($133F);
Application.OnException := HandleException;
end;
procedure TForm1.HandleException(Sender: TObject; E: Exception);
begin
Inc(Errors);
Caption := Format('Errors %d, msg: %s', [Errors, E.Message]);
Screen.Cursor := crDefault;
end;
type
TMyGeckoBrowser = class(TGeckoBrowser);
procedure TForm1.Loaded;
begin
inherited;
GeckoBrowser1.HandleNeeded;
(TMyGeckoBrowser(GeckoBrowser1).WebBrowser as nsIBaseWindow).SetVisibility(True);
end;
procedure TForm1.btnLoadUrlClick(Sender: TObject);
begin
try
GeckoBrowser1.LoadURI(edUrl.Text);
except
end;
end;

Looking at the headers, the prototype for Repaint is effectively as follows:
HRESULT __stdcall Repaint(PRBool force);
and that means that
procedure Repaint(force: PRBool); safecall;
is a reasonable declaration. Remember that safecall performs parameter re-writing to convert COM error codes into exceptions.
This does mean that if the call to Repaint returns a value that indicates failure, then the safecall mechanism will surface that as an exception. If you wish to ignore this particular exception then it is cleaner to do so at source:
try
baseWin.Repaint(True);
except
on EOleException do
; // ignore
end;
If you wish to avoid dealing with exceptions then you could switch to stdcall, but you must remember to undo the parameter re-writing.
function Repaint(force: PRBool): HRESULT; stdcall;
Now you can write it like this:
if Failed(baseWin.Repaint(True)) then
; // handle the error if you really wish to, or just ignore it
Note that Failed is defined in the ActiveX unit.
If you want to troubleshoot the error further then you can look at the error code:
var
hres: HRESULT;
....
hres := baseWin.Repaint(True);
// examine hres
Or if you are going to leave the function as safecall then you can retrieve the error code from the EOleException instance's ErrorCode property.

Related

AllocConsole, SetConsoleCtrlHandler prevent terminating

Before thinking this is a duplicate Question, then please read this: Yes this question is around in on SO in different languages at least Delphi, C# and C++ but they all have something in common: They talk about handling a clean shut down not preventing it.
So here we go:
Form a VCL application I open a new Console Window using AllocConsole but when closing that window with the cross in the top right corner my application terminates. That I would like to prevent not handle!
Some code:
function Handler(dwCtrlType: DWORD): Boolean; cdecl;
begin
case dwCtrlType of
CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_BREAK_EVENT:
Exit(True);
else
Exit(false);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AllocConsole;
SetConsoleCtrlHandler(#Handler, True);
end;
I have read the WinAPI documentation but it doesn't say anything about preventing Application termination.
I have tried adding message handlers for WM_ENDSESSION, WM_QUERYENDSESSION, WM_CLOSE and WM_QUIT on my MainForm but none of them gets called. I've also tried to add a FormCloseQuery event but it doesn't get called either.
I have read and tried out the solution found here but SetConsoleCtrlHandler(nil, True); doesn't provide the application for termination
So in short how to prevent termination.

Using Abort to improve/simplify code in some situations

I had a discussion the other day: https://stackoverflow.com/a/42156860/937125
where I didn't quite understand why an Abort was better than calling Exit in that situation. I tend not to use it in my code flow. I consider it a bad practice and bad for code flow.
but #David's statement in the comments made me wonder if maybe I was missing something:
Without a silent exception, how would you abort an operation when deep
down the call stack. For instance how would you abort a file copy
operation with a 10 deep call stack? Isn't that exactly what
exceptions are designed for? Sure you can code it without exceptions
but it is much more verbose and error prone.
I can't imagine such situation. Can someone give me an example of such code/scenario, and convince me that Abort in the above case is really a good thing and "much more verbose and error prone". (3-4 deep call stack is enough to illustrate)
The simplest scenario that illustrates my point is like so:
procedure MethodA;
begin
MethodB;
MethodC;
end;
procedure MethodB;
begin
// ... do stuff
end;
procedure MethodC;
begin
// ... do stuff
end;
That's fine as it is. Now suppose that MethodB asks the user for some input, and if the user presses the Cancel button, that no further work should be carried out. You could implement that like this:
procedure MethodA;
begin
if MethodB then
MethodC;
end;
function MethodB: Boolean;
begin
Result := MessageDlg(...)=mrOK;
if not Result then
exit;
// ... do stuff
end;
procedure MethodC;
begin
// ... do stuff
end;
That works fine, but imagine that you in the real world code, there was deeper nesting. The boolean returned by MethodB might need to be passed on up a great many levels. This would become cumbersome.
Or consider what happens if MethodB needs to return a value to its caller. In that scenario the original code might be like so:
procedure MethodA;
begin
MethodC(MethodB);
end;
function MethodB: string;
begin
Result := ...;
end;
procedure MethodC(Value: string);
begin
// ... do stuff with Value
end;
Now once more consider what happens if the user gets a chance to cancel. How can we return both a boolean and a string from MethodB? Using an out parameter for one of the return values? Using a compound structure like a record to wrap both values. The latter obviously involves lots of boilerplate so let us explore the former.
procedure MethodA;
var
Value: string;
begin
if MethodB(Value) then
MethodC(Value);
end;
function MethodB(out Value: string): Boolean;
begin
Result := MessageDlg(...)=mrOK;
if not Result then
exit;
Value := ...;
end;
procedure MethodC(Value: string);
begin
// ... do stuff with Value
end;
For sure you can do this, but this is beginning to look like the sort of code that exceptions were designed to simplify. And at this point, let us consider the existence of a silent exception, EAbort, raised by calling Abort, that does not result in a message being shown by the top level exception handler. That last point is what is meant by silent.
Now the code becomes:
procedure MethodA;
begin
MethodC(MethodB);
end;
function MethodB: string;
begin
if MessageDlg(...)<>mrOK then
Abort;
Result := ...;
end;
procedure MethodC(Value: string);
begin
// ... do stuff with Value
end;
The advantage is that MethodA does not need to worry about cancellation. And if the call stack was deeper, none of the methods between MethodA at the top, and MethodB at the point of user input, would need to know anything about cancellation.
A further benefit is that MethodB can retain its natural signature. It returns a string. In case of failure, either from a more traditional exception, or from user cancellation, an exception is thrown.
This very simple example isn't that much more compelling than the previous one that does not use Abort. But imagine what the code would look like if MethodB were 4 or 5 deep in the call stack?
I am absolutely not saying that Abort should always be used in place of exit. My belief is that both have their place. Where Abort shines is when the user opts to cancel an operation and you don't want any more processing to take place in the current event handler. Furthermore, since the user expressly opted to cancel, no further UI needs to be presented to them. You don't need a message box telling the user that they cancelled, they already know that.
Assume your program is doing a lengthy operation either in a separate thread or (even though it's frowned upon) calling Application.ProcessMessages. Now, you want the user to be able to abort that operation in a safe manner (that is: All resources are cleaned up, the data is in a consistent state etc.). So, the UI sets a flag somewhere and in your code you periodically check for that flag. If it is set, you call Abort or explicitly raise EAbort. This will cause all your carefully crafted try / except / finally blocks to be execute and making sure aborting the operation is safe.
// in the main thread:
procedure TMyProgressDialog.b_AbortClick(Sender: TObject);
begin
if AskUserIfHeIsSure then begin
gblAbortedFlag := true;
b_Abort.Enabled := false;
b_Abort.Caption := _('Aborting');
end;
end;
// call this repeatedly during the lenghty operation:
procecdure CheckAborted;
begin
// If you are in the main thread, you might want to call
// Application.ProcessMessages;
// here. If not, definitely don't.
if gblAbortedFlag then
Abort;
end;
Of course this could be done with a different exception, but I can't think of any other way to safely exit from a deep call stack without having to program lots of ifs and exits.

Delphi raise exception in constructor

SITUATION
I am going to write a class and the constructor is a custom one that I have made because I need to initialize some values. This is the code I've written so far:
type
TCombinatorio = class(TObject)
private
valN, valK: integer;
result: double;
public
property K: integer read valK;
property N: integer read valN;
constructor Create(valN: integer; valK: integer);
end;
constructor TCombinatorio.Create(valN: Integer; valK: Integer);
begin
inherited Create;
Self.valN := valN;
Self.valK := valK;
if ((valN < 0) or (valK < 0)) then
begin
raise Exception.Create('N and K must be >= 0');
end;
end;
Since I am going to do some math calculations, I need to avoid negative numbers.
QUESTION
Can I raise an exception in the constructor in that way? I am running the code in this way:
procedure TForm1.Button1Click(Sender: TObject);
var a: TCombinatorio;
b: string;
begin
a := TCombinatorio.Create(5,-2);
try
//some code
finally
a.Free;
end;
end;
As you can see here I have wrong parameters for my constructor, since the second is negative. I also cannot understand (according with the code of my constructor) if the a.Free inside the finally is really needed because when the constructor raises the exception, the destructor is called.
I thought to include the a := TCombinatorio.Create(5,-2); inside the try-finally block to avoid the problem but I am not sure. What do you think?
Your code is absolutely fine and correct. Raising exceptions from constructors is perfectly respectable. As you know the destructor is called.
You ask about this code:
a := TCombinatorio.Create(5,-2);
try
//some code
finally
a.Free;
end;
You are worried that Free will be called after the object has already been destroyed. That cannot happen. If an exception is raised in the constructor then it propagates up the call stack. That happens before the try block begins and so the finally block does not execute. Indeed the assignment to a does not happen.
Moving the creation inside the try would be disastrous and is in fact an incredibly common mistake. Suppose you did that:
// WARNING THIS CODE IS DEFECTIVE
try
a := TCombinatorio.Create(5,-2);
//some code
finally
a.Free;
end;
Now if an exception is raised then Free is called but on what? The variable a is not initialized. Even if it was, which it isn't, that would still be a double free.
OK, first you can raise an exception in the constructor, and yes it does call the destructor as a consequence. The code you show is fine. But I think you misunderstand what your code does. And to put the constructor inside a try finally block would be wrong. The point I think that you are missing is that if your constructor fails the try...finally block never gets executed and so the free is not executed. You should not call free if the constructor does not succeed, which is why you should not put the constructor inside the try...finally block.
First of all I would say that you cannot avoid exceptions in constructors so it cannot be an anti-pattern. If you check Delphi source code you will find number of places where exception is raised in constructor. For example
constructor TCustomForm.Create(AOwner: TComponent);
begin
// ... skipped some lines
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
The only thing you should know is that Delphi will automatically call the destructor if an exception escapes from the constructor. Actually it means that your destructor may be executed on a partially constructed object and it is your responsibility to write destructor properly. See TObject.Destroy documentation, and pay your special attention to the below quote:
Note: If an exception escapes from the constructor, the destructor is called to destroy the partially constructed object instance that
failed to initialize completely. Therefore, destructors should check
that allocated resources such as handles were actually allocated
before trying to release them, since their value might be zero.
PS In general you should assume that each line of code may raise an exception, but please do not be a paranoiac ;)
In that case I am usual add methods which check the data:
.. = class
function DataValid : boolean;
...
end;
Lots of benefit:
no exception in constructor. It simple copies its parameters to in-class fields.
special destructor for partially created class not needed.
simplicity of code.

Exception when calling DLL in delphi?

I have a procedure to call a function named [main()] from a DLL , this is the Caller procedure :
procedure call_dll(path:string);
var
lib: HMODULE;
mainfn: procedure(); stdcall;
begin
if FileExists(path) then
begin
try
lib := LoadLibrary(PAnsiChar(path));
Win32Check(lib <> 0);
try
#mainfn := GetProcAddress(lib, 'main');
Win32Check(Assigned(mainfn));
mainfn();
finally
FreeLibrary(lib);
end;
except
end;
end;
end;
Until yet every thing is working fine , I mean after writing the correct path of the DLL everything work without a problem but if I write a wrong path (other file path) in the path parameter it show me a popup error that this is is not a Win32 DLL but I don't want to bother the user with this type of errors , so I need a function to check the DLL and if it's not then it will automatically ask for another file again without showing the popup error ?
It is your code that is raising the exception. Your code makes an explicit choice to handle errors by raising exceptions. The exception is raised by your code here:
Win32Check(lib <> 0);
If you don't want to raise an exception, don't use Win32Check. Instead check the value of lib and handle any errors by whatever means you see fit.
The same issue is present for your other use of Win32Check.
Of course you are swallowing all exceptions with your catch all exception handler. A catch all exception handler is usually a bad idea. I don't understand why you have included that. I think you should remove it.
So if you are seeing dialogs when running outside the debugger it follows that the system is producing the dialogs. You should be disabling the system's error message dialogs by calling SetErrorMode on startup passing SEM_FAILCRITICALERRORS.
var
Mode: DWORD;
....
Mode := SetErrorMode(SEM_FAILCRITICALERRORS);
SetErrorMode(Mode or SEM_FAILCRITICALERRORS);
The somewhat clunky double call is explained here: http://blogs.msdn.com/b/oldnewthing/archive/2004/07/27/198410.aspx

Delphi debugger - go to line when exception happens

I got switched to other project at work and I noticed that Delphi XE2 debugger does not show the line that raised exception. When i got back at home i started to investigate. Then I found out that it can be disabled in Tools -> Options -> Debugger options and check Integrated debugging. Also I unchecked everything under Language exceptions in Exception types to ignore list. Notify on Language Exceptions left checked. Project -> Options -> Compiling, I have defaults there and Overflow and Range cheking enabled. I am running Debug build. I Cleaned it.
I have not noticed before, but now Delphi debugger doesn't give me the line when I call this code:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
_List: TStringList;
begin
_List := TStringList.Create;
try
Caption := _List[0]; // 'List index out of bounds (0)' here
finally
FreeAndNil(_List);
end;
end;
but this works (provided only to show that debugger does show the line for some things):
{$R+} // Range check is ON
procedure TForm1.BitBtn2Click(Sender: TObject);
var
_myArray: array [1 .. 5] of string;
i: integer;
begin
for i := 0 to 5 do
begin
_myArray[i] := 'Element ' + IntToStr(i); // Range check error here
ShowMessage('myArray[' + IntToStr(i) + '] = ' + _myArray[i]);
end;
end;
What is happening here? How to make the debugger to show as much as possible?
Thank you.
Let me answer the question first.
How to make the compiler show as much as possible
The compiler shows you that the error is in the call to the btnclick.
The trick is to put a breakpoint on the first line of the btnclick proc with F5.
Then rebuild (!) the application and run again.
The execution will stop that the breakpoint.
Step through the code using F8 until the error shows up.
Put a breakpoint F5 on the line that generated the error.
Abort and rerun the application.
When you get to the second breakpoint instead of pressing F8, press F7 to step inside the routine that causes the error, keep on pressing F7/F8 until you see what exactly is causing the problem.
Why is this happening?
The compiler traces back the source of the exception by following the stack trace.
Because in your case the code that generated the exception does not have stack trace (because it is not debug code), the compiler cannot do this trick and instead follows the stack trace that is does have; it moves one level up in your code and flags the exception there.
A look at this in detail
You're comparing apples and oranges.
This code (Exhibit A):
Caption := _List[0]; // 'List index out of bounds (0)' here
Has absolutely nothing in common with this code (Exhibit B):
_myArray: array [1 .. 5] of string;
....
_myArray[0]:= 'Hallo';
Exhibit A uses the TStringList class's items property, which is defined more or less as follows (I've simplified it a bit, but the fundamentals are correct):
type
TStringList = class(TStrings)
strict private
FList: array of string;
....
private
procedure Put(index: integer; const value: string);
function Get(index: integer): string;
published
property Items[index: integer]: string read Get write Put; default;
// ------------------------------------------------------^^^^^^^
....
end;
Notice the default keyword at the end of the Items property.
What this all means is that when you call _List[0], you are really calling _List.Items[0], which gets translated into Caption:= _List.Getitems(0), because of the read modifier on the property.
The default keyword allows you to omit the .Items.
The Get code looks like this:
function TStringList.Get(Index: Integer): string;
begin
if Cardinal(Index) >= Cardinal(FCount) then
Error(#SListIndexError, Index); <<-Here is the line that generates the error*
Result := FList[Index].FString;
end;
*Actually the error is generated inside the Error routine
Unless you have the RTL/VCL source code and you're running with debug DCU's you will not get a break on the exact trigger of the exception (which is inside the system.classes) unit.
Note that this error does not depend on range checking, it will always fire.
Because Delphi does not have debug info for the exact line where the error is generated, it does the next best thing and tries to make a guess.
Short version
The stringlist is a complex abstraction pretending to be an array.
Lots of code gets called, making the pinpointing of the error difficult for the compiler.
In Exhibit B:
_myArray: array [1 .. 5] of string;
....
i:= 0;
_myArray[i]:= 'Hallo';
Either a range check error or an access violation is generated.
Both of these errors occur on the exact line, allowing the compiler to stop at the correct spot.
Short version
The plain array is a basic building block with no hidden calls to code elsewhere, making pinpointing errors very easy for the compiler.
Understanding properties
class and record properties (and now class operators) look like simple assignments/operations to/with variables, but are in fact calls to (possibly complex) subroutines.

Resources