(first, I should note that this all is about Free Pascal 2.0.4. I didn't test it in Delphi, but that doesn't mean Delphi cannot have the same issues)
Did you know that there are types of exceptions that shoot straight through all your try..except blocks as if they just aren't there? :shock:
I stumbled onto one such pitfall a few days ago.
The host program loads a dll, they interact tightly, set alot of callback hooks so that it mostly the dll that calls the functions from the host program, not vice versa as it usually is...
You may remember that I convert strings to PChars in these functions and avoid the classes like plague. Because the class definitions are not the same in the host program and the dll, both having its own RTL and memory manager.
So, that's not the end of the story. The exception handling mechanisms are different too! And more, the exceptions in Pascal are classes too... Guess, what?.. (ExceptObject as Exception) in the host program and (ExceptObject as Exception) in the dll are totally different things. And not only that.
I use the matryoshka-like structure of nested Try..except blocks to build comprehensible error messages. Each except block adds its own opinion to the error log, then raises an exception to relay the event to the next block... Now imagine this situation:
The host program calls procedure A from the dll, which in turn calls the procedure B from the host program. In B, some crap happens and it calls my procedure Die(message: string);
Now, the Die, essentially does the following:
-- if dying flag not set then try to add (ExceptObject as Exception).Message to the log (plus, some info on GetLastError() on win32 platform).
-- add the message to the error log
-- set the dying flag
-- Raise Exception.Create('');
And guess what?.. The exception raised with the last line shoots right through all the elaborate traps and safeguards in the A procedure, and is caught only in the host program, in the first block that surrounds the call to A. The cleanup doesn't happen, the files are left open... many bad things.
So, I (aside from adding some elaborate callback dancing to determine whose ExceptObject is raised and passing its message safely) switched from the Pascal mechanism of raise Exception.Create('') to calling OS -- because those exceptions are caught normally in any block.
Ok, In Win32 it's RaiseException(0, 0, 0, nil); . But what for Linux...? Being lazy as I am, I didn't bother to search for its equivalent and simpy invoked
inc(Byte(nil^)); Blam! and Crash To Desktop, my program died instant death. :shock: :cry:
Hell, I didn't have such accident for years! My engine has so much layers of defense that you need at least a nuke (or something equally prominent like SIGKILL) to force it down without even a squeak. And there we are. Some shitty inc(Byte(nil^)); shoots it dead.
Next I tried in Win32 (Wine 0.9.36, to be exact). It worked without a notch. "W.T.F." I thought, and replaced it with
[pascal] procedure RaiseSystemException;
p: pinteger = nil;
And guess what?.. It works now in Linux without any complaints, displaying my own, OpenGL-based Blue Screen of Death, as intended.
Now, anybody knows why so dramatic difference between inc(Byte(nil^)); and invoking the same AV via variables?? :?: