Здравствуйте!
Ситуация следующая. Использую 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.* ошибка не воспроизводится, а оттуда тянется много кода.
Уважаемые! Если кто-то знает в чём может быть причины такой ошибки или есть идеи, в каком направлении копать, поделитесь, пожалуйста.
Заранее спасибо!
Пообщаюсь немного сам с собой. Надеюсь, кому-нибудь будет полезно.
В одной из 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 маска исключений меняется и исключение бросается.
Желаю всем пореже сталкиваться с таким гЭ. Пис!