How to determine where an exception is thrown during finalization in a Delphi application?
I work onsite with a client and I try to help them with a difficult problem. I hope there is a tool or function in Delphi that we can use to peek into the inner workings to help us find the problem.
Here is an overview of the problem we are dealing with. This is a commercial application that is currently deployed to Delphi 5. Over the past year, the application has been ported to Delphi XE. The migration is almost complete, but there are some serious bugs that are encountered.
The app itself is very large, with hundreds of devices and many third party and custom components. In one particular situation we are facing, the main form is created and then the application is terminated before the main form is displayed. The result is a crash that occurs during this completion as the modules are completed.
The debugger breaks the kernel32 RaiseException function that is thrown by NotifyNonDelphiException. We tried to set a non-breaking breakpoint that logs the call stack from the NotifyNonDelphiException, but that doesn't give us anything useful. The call stack contains only the methods that handle the exception, and these are RtlRaiseStatus and KUserExceptionDispatcher.
How do we identify the code that throws the original exception that is handled by the NotifyNonDelphiException?
edit: Here are two images captured after one instance of the exception. The first is the raised exception, and the second is the CPU window after closing the exception dialog.
It's been over a week since I posted this question and I am impressed with the various answers. Some of the comments on the first question were the most valuable, but some of the answers themselves are very informative.
My visit to the client has ended and I will ask them to review the responses that have been posted here. While we were unable to track down the actual source of the error, the cause of the error was more than obvious. Years of UI nudge without major refactoring left an application with erratic logging. When the log was canceled by the user, the main form was in a partial initialization state. When this process was not allowed to run its course, which happened when the user interrupted the login, there were very serious termination issues.
The company acquired AQTime Pro to help identify future issues, but the login process needs refactoring and will resolve the issue eventually.
At some point I decided to pull this question down, but I decided to keep it up to date as I believe others will find many great suggestions that have been posted informative.
I am currently accepting @Deltics' answer as I hate leaving the question unanswered. However, I ask viewers of this question to consider all other answers and comments as well, and they are equally valuable.
source to share
Exceptions should never allow "escape" from finalization (or initialization ) sections for this very reason.
With very few exceptions [sic], any code in the finalization clause must be enclosed in try..except. What you do when you get an exception is up to you, but at least calling OutputDebugString () will give you information when debugging and give you a point to set a breakpoint that will only cause a break when it happened actual exception.
finalization try // Perform finalization processing here except on e: Exception do OutputDebugString('%s: $s in unit %s', [e.ClassName, e.Message, 'MyUnitName']); end; end.
NOTE. The call to OutputDebugString () in this code refers to my own "friendly string" wrapper around a function in Windows, a unit extended to accept arguments.
Since you don't appear to have this kind of exception handling in the finalization sections , this will involve setting them before you can proceed. However, this exercise will improve the quality of your code in general and make it easier to diagnose any similar problems in the future (who's to say that once you've identified and fixed your current exception, that some other finalization of the exception won't raise its ugly head?).
In addition, the process of applying this exception handling will give you the opportunity to review each finalization section and determine if it can be handled differently, with the goal of eliminating as much finalization as possible .
This should not be considered "unnecessary overhead" or "waste of time", but an important part of the housekeeping to ensure the quality of your code to an acceptable standard.
An alternative approach
An alternative approach is to manage your own finalization list, as you did before the finalization sections were introduced. that is, in the initialization section of a block that has current termination, delete the current termination code in a procedure with no parameters and register this procedure with the "finalization manager".
Then, in your application, call the "finalization manager" at the appropriate time when your application shuts down to complete your finalization before actual finalization takes place. This will ensure that your completion routines are executed with a runtime exception handler still in place.
It also provides the ability to create a more complex "finalization manager" with mechanisms to ensure that the finalization procedures are executed in a specific, specific order (if necessary). This ability depends on how you implement your own finalization manager, of course.
source to share
Go to View / Debug Windows / Modules, find cxLibraryD15.bpl and extract its base add. Now subtract $ 00E51B9E - base = offset.
Start the app and suspend it immediately. Go to View / Debug Windows / Modules, find cxLibraryD15.bpl and extract its base addiss (it might be the same). Now add the offset from step 1 to it: base + offset = absolute address.
Open a breakpoint window or processor view and set a breakpoint at the address from step 2. You will now stop until the exception is thrown so you can see the call stack and analyze the situation in the debugger./ li>
source to share