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

Электронная таблица, демонстрирующая проблему, находится в пакете, на который указывает ссылка выше.

Кто-нибудь видел подобное поведение (да, описание очень расплывчатое, поэтому я понимаю, что трудно сказать...)?


person David I. McIntosh    schedule 24.08.2012    source источник
comment
Обновлен пример кода с гораздо более простым кодом. Сейчас очень тривиальная надстройка, примерно всего 100 строк кода на всю надстройку.   -  person David I. McIntosh    schedule 05.09.2012
comment
На самом деле всего 80 строк кода.   -  person David I. McIntosh    schedule 05.09.2012


Ответы (1)


Это было подтверждено Microsoft как ошибка в Excel 2003. Это происходит в большем количестве случаев, чем мой простой пример. Мой простой пример требовал выгрузки и перезагрузки надстройки, но у меня возникла ошибка в тех обстоятельствах, когда я просто выполнял вычисления диапазона. Кажется, это всегда происходит, когда кто-то пытается вычислить лист (shift-F9) после вычисления диапазона. Они не желают это исправлять. Я поражен, я не видел никаких других жалоб на эту ошибку.

person David I. McIntosh    schedule 15.09.2012