Как вернуть массив из XLL UDF

Я пытаюсь написать конструктор массива для Excel в виде функции листа, используя C API.

Цель: =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

Решение

Я понял. Несколько вещей, чтобы отметить здесь —

Вы должны убедиться, что ваша регистрация в 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;
}
}
2

Другие решения

Ваше решение неполное.

Ты пишешь

XLOPER12 list;

в теле функции. Это делает «список» локальной переменной, которая создается в стеке. Когда функция возвращается, «список» выходит из области видимости. Вы возвращаете указатель на переменную, которая больше не находится в области видимости, и когда Excel пытается получить доступ к этому указателю, поведение не определено. Если в Excel произойдет вызов другой функции перед обработкой возвращаемого значения, ваша переменная списка будет засорена.

Решение состоит в том, что вы должны либо динамически распределять память для списка (а затем убедиться, что она освобождается позже), либо сделать список статической или глобальной переменной.

1

Не уверен, почему вы думаете, что библиотека xll не позволит вам разрабатывать надстройку с открытым исходным кодом. IANAL, но Ms-PL, кажется, позволяет это. Вы смотрели на http://xllfunctional.codeplex.com/? Он использует аналогичные непереносимые приемы, которые не затягивают ускорение в решение. WINAPI / __stdcall — это то, что есть. Если вы знаете, как обстоят дела с кремнием, вы можете воспользоваться этим.

0
По вопросам рекламы [email protected]