如何从XLL UDF返回一个数组

4
我正在尝试使用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;
}
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;
    }
}

1
你应该修复你的解决方案。返回指向本地变量(列表)的指针是未定义行为。不过感谢提供 xlAutoFree12 解决方案。 - Arnaud

1
您的解决方案不完整。
您写道:
XLOPER12 list;

在函数体中,将“list”声明为本地变量并存储在堆栈中。函数返回后,“list”超出作用域。您正在返回一个指向不再处于作用域内的变量的指针,当Excel尝试访问此指针时,行为未定义。如果Excel在处理返回值之前恰好调用另一个函数,则您的list变量将被破坏。解决方法是要么动态分配list的内存(然后确保稍后释放它),要么使list成为静态或全局变量。

0
不确定为什么您认为xll库无法让您开发开源插件。IANAL,但Ms-PL似乎允许这样做。您看过http://xllfunctional.codeplex.com/吗?它使用类似的非可移植技巧,不会将boost带入解决方案中。WINAPI /__stdcall就是它所是的东西。如果您知道事物如何在硅下发生,那么可以利用它。

虽然我很感谢您提供的信息,但是您的评论似乎应该放在我的回答的评论部分。 - pyrospade
你应该注意到xllfunctional是你的项目 :) 我可能会误解这个,但是将XLL库与你的项目一起分发需要它遵循MS-PL或类似的许可证 - (D)如果你以源代码形式分发该软件的任何部分,你只能在你的分发中包含此许可证的完整副本才能这样做。如果您以已编译或目标代码形式分发该软件的任何部分,则只能在符合此许可证的许可证下才能这样做。 - pyrospade
xll库也是我编写的,如果这对解决您的问题有帮助的话。您是否认为CodePlex上提供的任何许可证都可以接受? - Keith A. Lewis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接