1

这是情况。我正在使用 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.cppcSomeClass以任何方式使用。这是代码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()),它是"Runtime Error 6, Overflow"在 VBA 中引起的。然后我开始怀疑这个错误可能与 FPU 寄存器有关(我对此知之甚少)。当我添加这 3 行时,我调用_controlfp()- 时引发了异常"0xC0000090: Floating-point invalid operation."。如果我保留上面显示的代码,异常会导致堆栈展开(我们没有到达该行),并且在 Excel 中显示VARIANT res;一个窗口。"runtime error 6. Overflow."综上所述,加上这3行导致更早抛出浮点异常。如果我们捕获第 3 行中抛出的异常(带有__except子句)并忽略它(通过 callng _clearfp()),那么 Excel 中不会报告错误。

一个讨厌的细节:如果我删除文件cSomeClass.cppcSomeClass.h从 Visual Studio 项目中删除,则不会重现错误。cSomeClass.h不包含在mydll.cpp中,但如果我cSomeClass.*从项目中删除文件,则 dll 大小会显着减小。

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

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

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

4

2 回答 2

2

因此,我在 Excel 的各个方面都遇到了类似的随机问题,而你是我的英雄。

我发现通过调用_clearfp(),它会消除错误并正确返回值。我认为问题出在我到处使用_HUGE值来表示无效浮点数的事实。它设置了标志,并导致 Excel(并且只有 excel)出错。在任何其他程序中,我从未遇到过运行时异常的任何问题。

于 2014-12-06T21:03:42.300 回答
1

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 );

这会导致运行时错误。

于 2012-07-30T11:28:32.173 回答