Вызов DLL функции из VBA, после чего вылазит Runtime Error 6 - overflow
От: PimpDaddy Россия  
Дата: 27.07.12 06:52
Оценка: 6 (1)
Здравствуйте!

Ситуация следующая. Использую 2005ю студию и Excel2007. Есть dll, из которой Excel VBA вызывает функцию возвращающую VARIANT. Эта функция вроде как успешно отрабатывает.
Но вот незадача: буквально в следующей строчке VBA кода вылазит окошко "runtime error 6. Overflow."

Ошибка ведёт себя очень странно. Я за ней уже наблюдаю пару дней, выявились следующие "факты":

1) Ошибка вылазит только после первого вызова функции из ДЛЛ. Все последующие разы отрабатывают без этой ошибки.

2) Ошибка вылазит на первой floating-point инструкции VBA после вызова функции из DLL.

dMinValue = 10000000#



3) Проект моей дллки сейчас состоит из 4х файлов
mydll.cpp
mydll.def
cSomeClass.cpp
cSomeClass.h

cSomeClass — достаточно сложный класс, использующий код из статических либ. КОД mydll.cpp никак не использует cSomeClass. DllMain просто делает "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);

  _controlfp(u, _MCW_EM);

  return res;
}


Сначала первых трёх строчек не было, ошибка оверфлоу — была. Потом я заподозрил, что эта ошибка может быть связана с FPU регистрами (о которых я оч. мало знаю). Когда я добавил эти три строчки на третьей из них стало бросаться исключение "0xC0000090: Floating-point invalid operation.". Если оставить всё, как приведено в коде, мы с эксепшеном вылетаем из функции, а в Экселе видим всё то же окно "runtime error 6. Overflow." Если же эксепшен, вылетающий из 3й строчки поймать и задавить (через _clearfp()), то никаких окошек с ошибками в экселе нет.

ЧТО САМОЕ ПОДЛОЕ: если выкинуть из проекта файлы cSomeClass.cpp и cSomeClass.h, то ошибка не вылазит. cSomeClass.h не инклюдится в mydll.cpp, но если убрать файлы cSomeClass.* из проекта, то размер длл-ки сильно сокращается.

Я пока что подозреваю, что есть какие-то статические объекты определённые в lib-ах, используемых в cSomeClass.cpp. Видимо эти объекты инициализируются в момент загрузки ДЛЛ (судя по экспериментам, до вызова DllMain) и где-то выставляют флаг ошибки "0xC0000090: Floating-point invalid operation.". Когда мы возвращаемся в Visual Basic, кто-то вызывает _controlfp и включает разные флоатинг-поинт исключения (в С++ они выключены), бросается сохранённое "0xC0000090: Floating-point invalid operation." и каким-то образом превращается в "Runtime Error 6. Overflow."

Маленький пример, воспроизводящий проблему, сделать не получилось, потому что без cSomeFile.* ошибка не воспроизводится, а оттуда тянется много кода.

Уважаемые! Если кто-то знает в чём может быть причины такой ошибки или есть идеи, в каком направлении копать, поделитесь, пожалуйста.

Заранее спасибо!
vba dll _controlfp
Re: Вызов DLL функции из VBA, после чего вылазит Runtime Error 6 - overflow
От: PimpDaddy Россия  
Дата: 27.07.12 15:15
Оценка: 6 (1)
Пообщаюсь немного сам с собой. Надеюсь, кому-нибудь будет полезно.

В одной из LIB оказалась глобальная переменная типа double, которую линкер не соптимизировал.
Эта переменная инициализируется примерно так:

const double x = std::numeric_limits<double>::signaling_NaN();


Инициализация происходит в функции _CRT_INIT(), которая вызывается до DllMain. Я ещё не до конца разобрался, но вставляя разный код и вызывая _statusfp() я понял, что эта инициализация изменяет "floating-point status word". Конкретно эта строчка выставляет бит _EM_INVALID. Ну а если кто-нибудь потом поменяет маску FPU исключений и заенэйблит эти исключения, то FPU исключение и полетит.

Видимо, при входе в Dll все FPU исключения отрублены, инициализация переменной выставляет флаг _EM_INVALID, по возвращению в VBA маска исключений меняется и исключение бросается.

Желаю всем пореже сталкиваться с таким гЭ. Пис!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.