1

Excel 2003,Windows XP SP3:

我们有许多 Excel 用户使用非常大、复杂的 Excel 工作簿。一个典型的工作簿可能有 60 多个工作表,大小超过 70MB。我们有一个 .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);
}

演示问题的电子表格位于上面链接指向的包中。

有没有人见过类似的行为(是的,描述很模糊,所以我意识到很难说......)?

4

1 回答 1

0

这已被 Microsoft 确认为 Excel 2003 中的一个错误。它发生在比我的简单示例更多的情况下。我的简单示例需要卸载和重新加载插件,但我遇到的错误是我只是在进行范围计算的情况。当人们在范围计算之后尝试工作表计算(shift-F9)时,似乎总是会发生这种情况。他们不愿意修复它。我很惊讶我没有看到任何其他关于这个错误的投诉。

于 2012-09-15T02:12:39.810 回答