Я пытаюсь написать конструктор массива для 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;
}
Я понял. Несколько вещей, чтобы отметить здесь —
Вы должны убедиться, что ваша регистрация в 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;
}
}
Ваше решение неполное.
Ты пишешь
XLOPER12 list;
в теле функции. Это делает «список» локальной переменной, которая создается в стеке. Когда функция возвращается, «список» выходит из области видимости. Вы возвращаете указатель на переменную, которая больше не находится в области видимости, и когда Excel пытается получить доступ к этому указателю, поведение не определено. Если в Excel произойдет вызов другой функции перед обработкой возвращаемого значения, ваша переменная списка будет засорена.
Решение состоит в том, что вы должны либо динамически распределять память для списка (а затем убедиться, что она освобождается позже), либо сделать список статической или глобальной переменной.
Не уверен, почему вы думаете, что библиотека xll не позволит вам разрабатывать надстройку с открытым исходным кодом. IANAL, но Ms-PL, кажется, позволяет это. Вы смотрели на http://xllfunctional.codeplex.com/? Он использует аналогичные непереносимые приемы, которые не затягивают ускорение в решение. WINAPI / __stdcall — это то, что есть. Если вы знаете, как обстоят дела с кремнием, вы можете воспользоваться этим.