3

我正在尝试使用 C API 为 Excel 编写一个数组构造函数作为工作表函数。

目标:=array_cons(1, 2, 3) => {1, 2, 3}

但是,我没有正确初始化 XLOPER12。在 Excel 中,我的函数当前返回#NUM. 我正在获取参数列表并vargs通过宏将其打包到一个数组中,然后尝试返回提供的数组部分。

#include <windows.h>
#include <xlcall.h>
#include <framewrk.h>
#include <boost/preprocessor.hpp>

#define VARG_COUNT 250
#define VARG_FORMAT(Z, A, B) B##A, 
#define VARG_DEF_LIST(N) BOOST_PP_REPEAT(N, VARG_FORMAT, LPXLOPER12 varg) \
                         LPXLOPER12 varg##N
#define VARG_ARRAY(N) { BOOST_PP_REPEAT(N, VARG_FORMAT, varg) varg##N }
#define GET_VARGS VARG_ARRAY(VARG_COUNT)

__declspec(dllexport) LPXLOPER12 WINAPI array_cons(VARG_DEF_LIST(VARG_COUNT))
{
    LPXLOPER12 vargs[] = GET_VARGS;
    int args_passed = 0;
    for(int i = 0; i < VARG_COUNT; ++i, ++args_passed)
    {
        if (vargs[i]->xltype == xltypeMissing)
        {
            break;
        }
    }
    if (args_passed == 0)
    {
        XLOPER12 err;
        err.xltype = xltypeErr;
        err.val.err = xlerrValue;
        return (LPXLOPER12)&err;
    }
    XLOPER12 list;
    list.xltype = xltypeMulti;
    list.val.array.lparray = (XLOPER12*)vargs;
    list.val.array.rows = args_passed;
    list.val.array.columns = 1;
    return (LPXLOPER12)&list;
}
4

3 回答 3

2

我想到了。这里有几点需要注意——

您需要确保您的 UDF 注册使用正确的签名。就我而言,我希望 Excel 引用给我它们各自的值,所以我Q在注册函数时使用了类型。如果您不明白这一点,请查看http://msdn.microsoft.com/en-us/library/office/bb687869.aspx

为了返回一个数组,您必须为list.val.array.lparray成员动态分配新内存并迭代地填充它。

__declspec(dllexport) LPXLOPER12 WINAPI array_cons(VARG_DEF_LIST(VARG_MAX)) {
    LPXLOPER12 vargs[] = GET_VARGS;
    int args_passed = 0;
    for(int i = 0; i < VARG_MAX; ++i, ++args_passed) {
        if (vargs[i]->xltype == xltypeMissing) {
            break;
        }
    }
    XLOPER12 list;
    list.xltype = xltypeMulti | xlbitDLLFree;
    list.val.array.lparray = new XLOPER12[args_passed];
    list.val.array.rows = args_passed;
    list.val.array.columns = 1;
    for(int i = 0; i < args_passed; ++i) {
        list.val.array.lparray[i] = *vargs[i];
    }
    return &list;
}

由于我们动态分配内存,我们需要定义回调来释放它。

__declspec(dllexport) void WINAPI xlAutoFree12(LPXLOPER12 p) {
    if (p->xltype == (xltypeMulti | xlbitDLLFree)) {
        delete [] p->val.array.lparray;
    }
}
于 2013-01-19T19:22:03.997 回答
1

您的解决方案不完整。

你写

XLOPER12 list;

在函数体中。这使“列表”成为在堆栈上创建的局部变量。当函数返回时,“list”超出范围。您正在返回一个指向不再在范围内的变量的指针,并且当 Excel 尝试访问此指针时,行为未定义。如果 Excel 在处理返回值之前碰巧调用了另一个函数,您的列表变量将被破坏。

解决方案是您要么必须为 list 动态分配内存(然后确保它稍后被释放),要么让 list 成为静态或全局变量。

于 2013-11-07T16:35:39.540 回答
0

不知道为什么您认为 xll 库不允许您开发开源插件。IANAL,但 Ms-PL 似乎允许这样做。你看过http://xllfunctional.codeplex.com/吗?它使用了类似的非便携式技巧,不会将 boost 拖入解决方案。WINAPI/__stdcall 就是这样。如果你知道事情是如何发生在硅片上的,你就可以利用它。

于 2013-02-25T02:38:38.197 回答