繁体   English   中英

从VBA调用dll函数时出现浮点错误

[英]Floating-point error when calling dll function from VBA

情况就是这样。 我正在使用MS Visual Sturio 2005和Excel 2007.我创建了一个包含函数的DLL

extern "C" VARIANT WINAPI MyFunc()
{
  VARIANT res;
  memset(&res, 0, sizeof(VARIANT));
  ::VariantInit(&res);    
  return res;
}

这是从Excel VBA调用的。 此功能似乎正常工作。 但是有一个问题:当VBA从我的函数返回并尝试执行下一个浮点指令时,我看到一个窗口:

运行时错误6.溢出。

这个错误很奇怪。 我在过去几天一直在调查,这里是我收集的“事实”:

1)错误仅在第一次调用dll后显示。 对此函数的所有后续调用都不会导致此错误。

2)控件从dll返回后,VBA代码中的第一个(看似无害的)浮点指令触发错误:

Dim dMinValue As Double
dMinValue = 10000000#

3)构建dll的项目包含4个文件:mydll.cpp,mydll.def,cSomeClass.cpp和cSomeClass.h。

cSomeClass是一个相当复杂的类,它从我的其他库中调用代码。 但是mydll.cpp不以任何方式使用cSomeClass 这是mydll.cpp的代码:

#include <OAIdl.h>
#include <float.h>
BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved) 
{
  return TRUE;
}
extern "C" VARIANT WINAPI MyFunc()
{
  unsigned int u;
  u = _controlfp(0, 0);
  _controlfp(u & ~(_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT), _MCW_EM);

  VARIANT res;
  memset(&res, 0, sizeof(VARIANT));
  ::VariantInit(&res);

  return res;
}

MyFunc()的初始版本没有前3行(与_controlfp()混乱的那些),它在VBA中导致"Runtime Error 6, Overflow" 然后我开始怀疑这个错误可能与FPU注册表有关(我对这些很少了解)。 当我添加这3行时,当我调用_controlfp() - "0xC0000090: Floating-point invalid operation."时抛出异常"0xC0000090: Floating-point invalid operation." 如果我保留上面显示的代码,异常导致堆栈展开(我们没有到达VARIANT res;行)和一个窗口说"runtime error 6. Overflow." 在Excel中显示。 总结一下,添加这3行会导致浮点异常提前抛出。 如果我们捕获第3行中引发的异常(带有__except子句)并忽略它(通过callng _clearfp() ),则Excel中不会报告错误。

cSomeClass.cpp DETAIL:如果从Visual Studio项目中删除文件cSomeClass.cppcSomeClass.h ,则不会再现错误。 cSomeClass.h未包含在mydll.cpp ,但如果从项目中删除cSomeClass.*文件,则dll大小会显着减少。

我在这一点上最好的猜测是cSineClass.cpp引用的LIB-s中有一些静态对象导致这种情况。 也许这些对象在加载dll时被初始化(构造)(根据我的实验,在DllMain之前)并且这会以某种方式导致"0xC0000090: Floating-point invalid operation."的错误标志"0xC0000090: Floating-point invalid operation." 被设定。 当我们从dll返回到Visual Basic时,有些人会调用_controlfp并在启动"0xC0000090: Floating-point invalid operation."时启用浮点异常(它们在С++中关闭) "0xC0000090: Floating-point invalid operation." 以某种方式转换为"Runtime Error 6. Overflow." 我在Excel中看到的。 这只是猜测。 unti现在我还没有找到任何可以做到这一点的静态对象。

不幸的是,我无法创建一个重现这个错误的小例子,因为只有当我有cSomeFile.*作为项目的一部分时它才显示出来。 那些文件需要我所有的库...

如果有人知道这种行为可能是什么原因,或者有关于如何推进我的调查的建议,将不胜感激。

所以我在各个方面都有与Excel类似的随机问题,你就是我的英雄。

我发现通过调用_clearfp() ,它会使错误安静并正确返回值。 我认为这个问题源于我在整个地方使用_HUGE值来表示无效的浮点数。 哪个设置了标志,并导致Excel(并且只有excel)到barf。 在任何其他程序之前,我从未遇到过运行时异常的任何问题。

1)显然,当我从VBA进入DLL代码时,FPU异常被关闭,就像有人打电话一样

  u = _controlfp( 0, 0 );
  _controlfp( u & ((_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT )), _MCW_EM );

在将控制传递给我的DLL代码之前。

2)在任何点调用std :: numeric_limits :: signaling_NaN()都会导致_EM_INVALID标志在浮点状态字中设置(可以通过调用_statusfp()看到)。

3)当从DLL返回控件时,似乎打开了FPU异常。 好像有人打来电话:

  u = _controlfp( 0, 0 );
  _controlfp( u & (~(_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | EM_UNDERFLOW | _EM_INEXACT )), _MCW_EM );

这会导致运行时错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM