Probably many have met with such a "partisan" at the start or end of the application:

A very informative message, immediately understand the cause of the error, the place and the way to solve it.
However, if no joke, what is it all about?
Of course, this is an exception, but neither the type of the exception nor its description is available to us - just “Runtime error 217” and the address, and then ourselves ...
')
To be honest, before, I somehow did not even think about this exception, because in my projects, it is a rare phenomenon, until one day the whole series of users started to reproduce exactly the 217th error.
However, even then I did not go the right way and just added an additional level of logging to the project, according to the results of which I quickly found the cause and corrected it.
But, in fact, I just spent my time ...
And I would spend it in the future, if the other day Viktor Fedorenkov did not contact me and did not tell about his thoughts on the error number 217.
Theory and problem analysis
Without theory, we are nowhere, otherwise we can face the limits of our own knowledge.
Therefore, we begin, of course, with the theoretical part.
For a start, I refreshed my ideas about errors in principle, after re-reading part of the article
“Error Handling - Chapter 1.2.2” by Alexander Alekseev, from which I learned that error 217 would be displayed if the SysUtils module was not initialized, moreover, Alexander has this illustrated quite clearly:
View full size image ...Based on this picture, you can make a rough conclusion: while SysUtils is alive - all exceptions should be displayed in the normal form, which is a separate mention:
For example, if you see a message about a runtime error, then, judging by the above scheme, it is unlikely that an error occurs in event handlers on the form. But it is much more likely that it occurs, say, in some kind of finalization section (which is performed after the finalization section of the SysUtils module) or in the assigned ExitProcessProc procedure. But, of course, the cause of the error can sit anywhere - including in the event handlers mentioned.
Well, let's check, write the code in which SysUtils should be finalized after Unit1, in which we artificially generate an exception:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) private public end; var Form1: TForm1; implementation {$R *.dfm} initialization finalization raise Exception.Create('finalization exception'); end.
Build, run, close the form and ... Runtime error 217.
The statement that 217 is displayed after finalizing SysUtils is completely true, but let's look at the finalization code itself:
procedure FinalizeUnits; ... begin ... Count := InitContext.InitCount; Table := InitContext.InitTable^.UnitInfo; ... try while Count > 0 do begin Dec(Count); InitContext.InitCount := Count; P := Table^[Count].FInit; if Assigned(P) then ... TProc(P)(); ... end; end; except FinalizeUnits; raise; end; end;
See what happens: the FinalizeUnits procedure calls all finalizing procedures whose addresses are located in the InitContext.InitTable ^ .UnitInfo array in the order in which they were initialized, i.e. the first ones are located at the beginning of the array (and finalization comes from the end).
Somewhere at the very bottom is SysUtils + System, and we, with our Unit1 module, are somewhere at the top.
But suddenly an exception occurs in our module and "women", the order of catharsis is broken.
After the "broads", FinalizeUnits is called again, skipping our module that caused the exception, as a result of which SysUtils and various class destructors found along the way are destroyed, System crashes to the heap with the memory manager (sitting at the top of the list), after which there is a control shot to the forehead - RAISE, this is where we sailed - hello 217.
But what if an exception occurs in the initialization section of any module?
Yes, all the same:
procedure InitUnits; ... begin ... try ... except FinalizeUnits; raise; end; end;
We conclude: any
unhandled exception in the initialization or finalization sections will result in the loss of the exception description and result in error 217.
On this with theory, I think, we will finish.
Having an understanding of the reason for the occurrence of Runtime error 217, let's try to get our hands on a more familiar version of the message about the exception.
Disable module finalization
At the very beginning of the discussion, Viktor proposed a rather effective way of circumventing this error.
His analysis was as follows: the general initialization of the exception handler is performed in the InitExceptions procedure of the SysUtils module, and the finalization is done with the DoneExceptions call.
If in any way you disable the call to DoneExceptions plus prevent the memory manager from collapsing by blocking the call to the System finalization block, we will receive an exception message in an acceptable form.
As a solution, the following code was proposed, which needs to be connected to the project file by the very first module (will work starting from D2005 and higher):
unit suShowExceptionsInInitializeSections; interface uses SysUtils; implementation uses Windows;
To be honest - I applauded standing.
Here it is:
hack in the dirtiest form as it is - such things can be done only by those who truly understand what this means :)
And this module brought the work of our IT department to about three hours - it was a tough discussion :)
But, by the way, let's analyze the logic of this code:
Its essence is simple, you need to go to the data on the loaded modules (including BPL) in the form in which the Delphi application understands them. This was done by accessing the start of the unidirectional list of TLibModule structures. The first element of the list will be a structure that describes the current image, from where we just need to get data on the UnitInfo structure, which contains data on both the number of initialized modules and the addresses of their initialization and finalization procedures as a PackageUnitEntry entry.
Blocking module finalization occurs by setting the FInit parameter to
nil for each PackageUnitEntry entry.
When numbing this parameter, FinalizeUnits will not be able to make a call to the handler, and as a result, the same
raise I wrote about above will be able to correctly display the raised exception.

But then everything is more complicated.
Trying to brush a good thought.
The idea is sound and the reasons are clear, but somehow, the resources are still not released, FastMem will stop working normally (it collects leaks just when finalizing), and compatibility is not enough, for example, as I said above, under Delphi 7 This code will not work at all.
After the first hour of discussions in the IT department, we even managed to come to the following conclusion: “Yes, to hell with them with SysUtils and System - they don’t carry anything critical.”
And then, they began to argue again - well, this approach did not suit us, everything seemed to be fine, but not neatly somehow.
There were even considered options for direct splicing of blocks of finalization and to the Exception destructor pile - but the additional hack did not suit anyone at all on an existing hack.
And here, sitting in the debugger and running the code on the 70th time, the thought came.
So this ... and how in general the message on an exceeding is displayed?
And it is displayed by transferring control to ExceptHandler, in the code of which there is nothing secret.
And what are we doing removing the finalization of the modules?
That's right, we force him to volunteer.
Let's try to emulate the ExceptHandler call.
We write a test unit and connect it to the project the very first:
unit Test; interface uses SysUtils; var E: Exception; implementation initialization finalization E := AcquireExceptionObject; if E <> nil then begin ShowException(E, ExceptAddr); E.Free; Halt(1); end; end.
Launch and ...

Happened.
Having integrated into the finalization cycle, we displayed the exception that occurred and continued the finalization further by calling Halt (1).
As a result, the problem is solved, correctly and documented, and compatible with Delphi 7, but ...
And if not to develop an idea?
There is such a thing as “induced errors”, i.e. errors occurred due to the fact that before them, too, an error occurred.
Well, for example, function A, which should return an instance of a certain class and function B, which uses this instance in its work. For example, in function A, an unhandled exception occurred (for example, there was no access to the file) and it did not create a class, and then somewhere much later, using the application code, procedure B calls the instance and Access Violation occurs as a result.
The same can happen in the initialization / finalization procedures, and the exception that occurred in the finalization will hide from us the very reason.
For the demonstration, we will write the following code, in which, when the application is initialized, a certain logger will be created, into which the operation stages of the application will be written, and in the finalization there will be a record of completion.
To generate an exception, let the logger be created using a nonexistent path:
uses Classes; var Logger: TFileStream; const StartLog: AnsiString = ' ' + sLineBreak; EndLog: AnsiString = ' ' + sLineBreak; implementation initialization Logger := TFileStream.Create('A:\MyLog,txt', fmCreate); Logger.WriteBuffer(StartLog[1], Length(StartLog)); finalization Logger.WriteBuffer(EndLog[1], Length(EndLog)); Logger.Free; end.
Few people have “A” on the system, so the result of this code will be either “Runtime error 216” (specifically 216, not 217), or if we include the code from the previous chapter:
Exception EAccessViolation in module Project2.exe at 001B1593.
Access violation at address 005B1593 in module 'Project2.exe'. Read of address 00000000.
But the reason is that lies in the very first exception, which is not displayed by us and with a swoop to understand the cause of the error will not work.
In order to correct this injustice, you can brush the code a bit and bring it to this state:
unit ShowExceptSample; interface uses SysUtils, Classes; implementation type PRaiseFrame = ^TRaiseFrame; TRaiseFrame = packed record NextRaise: PRaiseFrame; ExceptAddr: Pointer; ExceptObject: TObject; ExceptionRecord: PExceptionRecord; end; var
Here the idea is simple, the GetNextException function essentially repeats the call to AcquireExceptionObject, but after its call does not lose the reference to the next exception in the queue, it stores the address of the next frame in an external variable.
After that, all exceptions are entered in the list (the most recent will be the first in the list) and are displayed to the programmer in accordance with the sequence, with the result that we will immediately understand what happened here first:

And only after him did all sorts of AVs go there.
Now for the rest of the error codes.
Why did I start with “Runtime error 217”?
Well, because it is the most easily reproducible, and so technically, using the above module, we will get into our hands a completely normal description of all possible Runtime errors, of which we have how many:
reMap: array [TRunTimeError] of Byte = ( 0, 203, 204, 200, 201, 215, 207, 200, 205, 206, 219, 216, 218, 217, 202, 220, 221, 222, 223, 224, 225, 227, 0, 228, 229, 235, 236 {$IFDEF PC_MAPPED_EXCEPTIONS} {$ENDIF PC_MAPPED_EXCEPTIONS} {$IF defined(PC_MAPPED_EXCEPTIONS) or defined(STACK_BASED_EXCEPTIONS)} {$ENDIF} {$IF Defined(LINUX) or Defined(MACOS)} , 233 {$ENDIF LINUX or MACOS} {$IFDEF POSIX} , 234 {$ENDIF POSIX} , 237, 238 );
Total
With such a careless code, we can get what we don’t want to say about the error code 217.
However, I do not think that this approach will be unfamiliar to experienced programmers.
Most likely this is hello bike, because most likely this problem has already been solved by someone earlier, but I just did not know about this solution.
And if not, then I will be the second.
A separate respect to the co-author and inspirer of this article is Viktor Fedorenkov.
Good luck.