Excel зависает... еще одна ошибка дерева оценки?
Excel 2003, Windows XP SP3:
У нас есть несколько пользователей Excel, которые работают с очень большими и сложными книгами Excel. Типичная рабочая книга может иметь более 60 рабочих листов и иметь размер более 70 МБ. У нас есть расширение.xll, которое мы производим и используем во всех этих таблицах. У нас есть широкие возможности отладки и регистрации с нашим дополнением. Мы производим версии аддина и используем их почти 10 лет. Инфраструктура для нашего интерфейса Excel, таким образом, доказала свою надежность и стабильность.
В определенных ситуациях Excel зависает в определенном фрагменте кода; то есть он висит внутри кода в самом Excel и висит на одном конкретном фрагменте кода. В этом случае существует жесткий цикл кода (может быть, 20 машинных инструкций без "вызовов"), который он выполняет бесконечно. Код, кажется, идет по связанному списку, который является круглым, часто с несколькими узлами, например, только с четырьмя узлами. Я смотрю на меня так, будто он проходит по связанному списку, который соответствует (какой-то части) дереву оценки - хотя я совсем не уверен.
Мне удалось продемонстрировать поведение с помощью невероятно простого надстройки (3 зарегистрированных функции, которые возвращают фиксированные данные) и невероятно простой электронной таблицы (5 ячеек, 3 формулы с использованием 3 функций надстройки). Excel зависает только после завершения оценки всех ячеек на листе и после обновления экрана с результатами. Он зависает независимо от того, какая версия Excel SDK используется (v5.0, v12.0 и v14.0 все протестированы).
Вот ссылка на образец кода: образец рабочей книги.xls и соответствующий.xll надстройка и источник для надстройки
Источник для надстройки тривиален:
#include <windows.h>
#if SDK_VER == 5
#include "xlcall_v5_0.h"
#elif SDK_VER == 12
#include "xlcall_v12_0.h"
#elif SDK_VER == 14
#include "xlcall_v14_0.h"
#endif
extern "C" __declspec(dllexport) int xlAutoOpen(void) {
#define N_FUNCTIONS 3
#define N_FUNCTION_REGISTRATION_PARAMETERS 3
char* rgFuncs[N_FUNCTIONS][N_FUNCTION_REGISTRATION_PARAMETERS] = {
{"\013FunctionOne", "\002RP", "\013FunctionOne"},
{"\013FunctionTwo", "\002RP", "\013FunctionTwo"},
{"\014FunctionFour", "\003RBP", "\014FunctionFour"} };
XLOPER xlFuncParams[N_FUNCTION_REGISTRATION_PARAMETERS+1];
LPXLOPER ppxlFuncParams[N_FUNCTION_REGISTRATION_PARAMETERS+1];
Excel4(xlGetName, (ppxlFuncParams[0] = &xlFuncParams[0]), 0);
for(int i = 0; i < N_FUNCTIONS; i++) {
for (int j = 1; j <= N_FUNCTION_REGISTRATION_PARAMETERS; j++) {
xlFuncParams[j].xltype = xltypeStr;
xlFuncParams[j].val.str = rgFuncs[i][j-1];
ppxlFuncParams[j] = &xlFuncParams[j];
}
Excel4v(xlfRegister, 0, N_FUNCTION_REGISTRATION_PARAMETERS+1, ppxlFuncParams);
}
Excel4(xlFree, 0, 1, (LPXLOPER)&xlFuncParams[0]);
return 1;
}
extern "C" __declspec(dllexport) LPXLOPER xlAddInManagerInfo(LPXLOPER xAction) {
static XLOPER xInfo;
XLOPER xloCoerceType, xIntAction;
xloCoerceType.xltype = xltypeInt; xloCoerceType.val.w = xltypeInt;
Excel4(xlCoerce, &xIntAction, 2, xAction, (LPXLOPER)&xloCoerceType);
if( xIntAction.xltype == xltypeInt && xIntAction.val.w == 1) {
xInfo.xltype = xltypeStr;
xInfo.val.str = "\010ExcelBug";
} else {
xInfo.xltype = xltypeErr;
xInfo.val.err = xlerrValue;
}
return (LPXLOPER)&xInfo;
}
LPXLOPER XLReturn(const double* pdData, const int nData, bool bRow=false) {
static XLOPER xlDLLResult;
static int nAllocated = 0;
if( nAllocated==0 ) {
xlDLLResult.xltype = xltypeMulti;
nAllocated=max(nData,2);
xlDLLResult.val.array.lparray = new XLOPER[nAllocated];
} else if( nAllocated < nData ) {
delete [] xlDLLResult.val.array.lparray;
nAllocated = nData;
xlDLLResult.val.array.lparray = new XLOPER[nAllocated];
}
for(int i = 0; i < nData; ++i) {
xlDLLResult.val.array.lparray[i].xltype = xltypeNum;
xlDLLResult.val.array.lparray[i].val.num = pdData[i];
}
xlDLLResult.val.array.rows = (WORD)(bRow? 1 : nData );
xlDLLResult.val.array.columns = (WORD)(bRow? nData : 1 );
return (LPXLOPER) &xlDLLResult;
}
extern "C" __declspec(dllexport) LPXLOPER FunctionOne( LPXLOPER ) {
double dData = 41144.;
return XLReturn(&dData, 1);
}
extern "C" __declspec(dllexport) LPXLOPER FunctionTwo( LPXLOPER ) {
const double pdData[2] = {41145., 41176.};
return XLReturn(pdData, 2);
}
extern "C" __declspec(dllexport) LPXLOPER FunctionFour( double, LPXLOPER ) {
const double pdData[2] = {41145., -0.01345212};
return XLReturn(pdData, 2, true);
}
Электронная таблица, демонстрирующая проблему, находится в пакете, указанном по ссылке выше.
Кто-нибудь видел подобное поведение (да, описание очень расплывчато, поэтому я понимаю, что трудно сказать...)?
1 ответ
Microsoft подтвердила это как ошибку в Excel 2003. Это происходит в большем количестве случаев, чем мой простой пример. Мой простой пример потребовал выгрузки и перезагрузки надстройки, но у меня возникла ошибка в тех случаях, когда я просто выполнял калибровку диапазона. Кажется, что это всегда происходит, когда кто-то пытается выполнить калькуляцию листа (shift-F9) после вычисления диапазона. Они не хотят это исправить. Я поражен, я не видел никаких других жалоб об этой ошибке.