Suppose you are calling some native code from your managed code, and you observe an Access Violation crash soon after returning from the native code inside your managed code. if you run the application inside the Visual Studio debugger you will see the following exceptions in the output window if IDE.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000090: Floating-point invalid operation.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000005: Access violation writing location 0x00133fec.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000091: Floating-point overflow.First-chance exception at 0x79f958e3 in TestApp.exe: 0xC0000091: Floating-point overflow.First-chance exception at 0x79f958e3 in TestApp.exe: 0xC0000091: Floating-point overflow.
First-chance exception at 0x79f958e3 in TestApp.exe: 0xC0000005: Access violation writing location 0x001331ec.
Or other Floating point exceptions with a Access violation at the end
These floating point exceptions gives a clue that somehow the FPCW (Floating Point Control Word) register has been changed inside the native code. Because its value has been changed so it results in floating point exceptions inside the managed code. Its stated in the ECMA specifications for IL that IL floating point operations never throw exceptions. So CLR is not hardened against floating point exceptions. Although it is allowed in native code to change the FPCW register, but you need to restore it back to the old state as it was in before it was changed.
So the expectation here is that the FPCW should be restored to the same value as it was earlier before entering into the native code. In scenarios where the native code is not restoring this register we can call CRT’s _controlfp() inside the managed code to reset FPCW soon after returning from native code.
Below is a sample code snippet which shows how to call _controlfp() using pInvoke:
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int _controlfp(int newControl, int mask);
const int _RC_NEAR = 0x00000000;
const int _PC_53 = 0x00010000;
const int _EM_INVALID = 0x00000010;
const int _EM_UNDERFLOW = 0x00000002;
const int _EM_ZERODIVIDE = 0x00000008;
const int _EM_OVERFLOW = 0x00000004;
const int _EM_INEXACT = 0x00000001;
const int _EM_DENORMAL = 0x00080000;
const int _CW_DEFAULT = (_RC_NEAR + _PC_53 +
_EM_INVALID + _EM_ZERODIVIDE +
_EM_OVERFLOW + _EM_UNDERFLOW +
_EM_INEXACT + _EM_DENORMAL);
Some_unmanahed_code(); /*this is the native code which alters the FPCW regiester */
int value = _controlfp(_CW_DEFAULT, 0xfffff); /* Calling _controlfp()using pInvoke */
Some_managed_call() /*Calling the maanged code using pInvoke */
I found this “initial Control Word” value as _CW_DEFAULT in the float.h file.
/* initial Control Word value */
#define _CW_DEFAULT ( _RC_NEAR + _PC_53 + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL)