在调用一个C++ Builder写的dll之后,操作另外的一个2D Chart控件后,报如下错误:
Message :算术运算中发生溢出或下溢。, Source:System.Windows.Forms, 在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
在 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)在 System.Windows.Forms.Form.ShowDialog()
从异常现象来看,显然和浮点数的运算有关。因为dll和2D控件本身是没有源码的,无法获知到具体异常原因。因此一个自然而然的想法是,可能是dll中某些函数更改了浮点数的设置后没有复位,导致此异常出现。
从网上资料看,确实也存在这种现象:
This solution is only for those guys who r working on different culture like Farsi, Urdu, Arabic and many other, When we change the culture in the .net frame work, they call some unmanaged code(dll's) which change the floating point of the compiler and didn't set it back to its original floating point.So we change the calling sequence to set floating point back to its original floating point before transferring the control to managed code.
为验证此想法,在调用dll中函数的前后,观察其浮点掩码的状态情况。
int status1 = _controlfp(0, 0);
......//调用dll的代码
int status2 = _controlfp(0, 0);
运行程序,获取到上述两个状态,得到:status1为0x0009001F,stauts2为0x000C0003。因此断定是组件dll更改了系统缺省的浮点掩码设置。
在VS中,有一个函数,用于设置浮点异常掩码,即_controlfp。其原型为:
unsigned int _controlfp( unsigned int new, unsigned int mask );
mask
不为零,控制字的新值被设置:对于在 mask
中打开的位(即等于 1),在 new
中,对应的位用于更新控制字。 fpcntrl
=
(fpcntrl
& ~mask
)|(new & mask
) 其中 fpcntrl
是浮点控制字。常用的mask如下:
掩码 | 十六进制值 | 常量 | 十六进制值 |
---|---|---|---|
_MCW_DN (不正常的控件) |
0x03000000 | _DN_SAVE _DN_FLUSH |
0x00000000 0x01000000 |
_MCW_EM (中断异常掩码) |
0x0008001F | _EM_INVALID _EM_DENORMAL _EM_ZERODIVIDE _EM_OVERFLOW _EM_UNDERFLOW _EM_INEXACT |
0x00000010 0x00080000 0x00000008 0x00000004 0x00000002 0x00000001 |
_MCW_IC (无限制控件)(不支持 ARM 或 x64 平台。) |
0x00040000 | _IC_AFFINE _IC_PROJECTIVE |
0x00040000 0x00000000 |
_MCW_RC (舍入控件) |
0x00000300 | _RC_CHOP _RC_UP _RC_DOWN _RC_NEAR |
0x00000300 0x00000200 0x00000100 0x00000000 |
_MCW_PC (精度控件)(不支持 ARM 或 x64 平台。) |
0x00030000 | _PC_24 (24 位)_PC_53 (53 位)_PC_64 (64 位) |
0x00020000 0x00010000 0x00000000 |
C#应用示例:
[DllImport("msvcr70.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int _controlfp(int n, int mask);
internal void ResetFPCR()
{
const int _EM_OVERFLOW = 0x0009001F;//为原来的默认值
const int _MCW_EM = 0x000FFFFF;//设置默认值相关的掩码为1,即可设置成默认值。
_controlfp(_EM_OVERFLOW, _MCW_EM);
}
void Test()
{
......//调用dll的代码
ResetFPCR();
}