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);
}
Электронная таблица, демонстрирующая проблему, находится в пакете, на который указывает ссылка выше.
Кто-нибудь видел подобное поведение (да, описание очень расплывчатое, поэтому я понимаю, что трудно сказать...)?