Recently, I stumbled onto a crash while working with the RAD Studio IDE.
I was in the midst of creating a package, and the IDE crashed!
What happened was that when creating, or opening a project, the IDE calls into GetMSBuildProjectInterop(const ProjectXml, FileName: System.UnicodeString): IMSBuildInterop. This is a .NET interop function that creates a COM interface that allows Delphi to use the MSBuild functionality provided by the .NET Framework.
The first time it happened, I captured the image you see below. The second time it happened, I captured another image, together with the FPU and 8087 registers, as seen below.
Why did the fld qword ptr [edi+$24] instruction trigger a crash?
fld qword ptr [edi+$24]
The data was invalid, but due to the FPU/8087 control mask, it triggered the crash.
If you're a Delphi developer working with COM, you need to pay close attention to a subtle but significant change introduced in RAD Studio Athens (R120). The default floating-point exception masks have been altered in a way that transforms a once-predictable error condition into a potential time bomb—one that can cause CoCreateInstance to crash your application with no warning whatsoever.
CoCreateInstance
Starting with RAD Studio Athens, Embarcadero changed the default floating-point exception settings across all platforms. For Windows 32-bit applications, the Default8087CW control word was changed from $1332** to **$033F.
Default8087CW
To understand why this matters, we need to look at what these values represent. The FPU control word contains bits that control precision, rounding mode, and—crucially—which floating-point exceptions generate signals when they occur. The old value ($1332) left certain exceptions unmasked: invalid operations, zero divides, and overflows would raise Delphi exceptions that your code could catch and handle. The new value ($033F) masks all floating-point exceptions.
Various calls in Delphi's RTL, and also within RAD Studio IDE's code, calls to various .NET interop and CoCreateInstance functions were not protected with a prolog of Set8087CW and epilog of Reset8087CW.
Here's where things get dangerous. When a floating-point operation encounters an error—say, a division by zero—the FPU doesn't just ignore it when exceptions are masked. Instead, it sets a status flag in the FPU status word indicating that an exception occurred. The exception isn't raised, but the pending exception state remains.
Now consider what happens when your code calls CoCreateInstance (or any COM API) after such an operation. COM objects and their surrounding infrastructure may change the FPU control word. If they unmask the very exception that's pending, the FPU will immediately generate a trap—and your application crashes.
This isn't theoretical. Developers have long encountered issues where CoCreateInstance fails with E_FAIL (0x80004005) or other cryptic errors that trace back to FPU state corruption. The Delphi RTL itself has contained workarounds—for example, CreateOleObject in 32-bit code explicitly sets the control word to mask overflow exceptions before calling CoCreateInstance.
E_FAIL
CreateOleObject
The change to Default8087CW creates a perfect storm of silent failures:
Exceptions are masked by default, so your code won't receive immediate feedback when something goes wrong with floating-point calculations.
Pending exception flags persist in the FPU, waiting for the right (or wrong) moment to trigger.
External code—including COM infrastructure, system DLLs, and third-party libraries—can change the FPU control word at any time.
CoCreateInstance is a common entry point into COM, and it's precisely the kind of call that can expose this hidden state.
Previously, developers could rely on Delphi's default behavior to raise exceptions and surface problems early. Now, those problems are swept under the rug—until a seemingly unrelated CoCreateInstance call brings them crashing to the surface.
If you're affected by this change, you have several options:
Restore the old exception behavior by calling SetExceptionMask early in your application
SetExceptionMask
Clear pending FPU exceptions before calling into COM code using ClearFPUExceptions or ClearExceptions(False).
ClearFPUExceptions
Be extremely careful with floating-point operations in code that precedes COM calls. Consider using Set8087CW or SetExceptionMask to manage the FPU state explicitly around such calls.
Set8087CW
Embarcadero's change was likely made to improve compatibility with other platforms and libraries that expect all exceptions to be masked. But in doing so, they've traded one set of problems for another. The Delphi RTL is now more dangerous than before—not because it's broken, but because it hides errors that can later manifest as inexplicable crashes in the most inconvenient places.
If you're using COM in your Delphi applications, especially on 32-bit Windows, take the time to audit your floating-point handling. Your future self—the one not debugging a production crash at 2 AM—will thank you.