Потокобезопасность функций XLL
У меня есть эта глупая функция XLL:
__declspec(dllexport) long F(long arg1, long arg2)
{
return arg1 + arg2;
}
Я думаю, с этим проблем нет, полученная функция Excel будет поточно-ориентированной.
Но как насчет функции, принимающей массивы и возвращающей массивы? Рассмотрим, например, функцию:
__declspec(dllexport) LPXLOPER12 WINAPI G(LPXLOPER12 arrayin1, LPXLOPER12 arrayin2)
{
static XLOPER12 xlArray; // the reference to this one is going to be returned
// treating arrayin1 and arrayin2 and outputting something in xlArray
return static_cast<LPXLOPER12>(&xlArray);
}
Очевидно, что static
это плохо для безопасности потоков и одновременного вызова в Excel. Как мне изменить мою функцию для обеспечения безопасности потока?
РЕДАКТИРОВАТЬ
Пример кода с использованием дескрипторов:
__declspec(dllexport) LPXLOPER12 WINAPI CreateComplex(LPXLOPER12 arrayin1, LPXLOPER12 arrayin2)
{
static XLOPER12 xlArray;
xlArray.xltype = xltypeStr;
if (arrayin1->xltype != xltypeNum)
{
xlArray.val.str = L"blah";
return static_cast<LPXLOPER12>(&xlArray);
}
if (arrayin2->xltype != xltypeNum)
{
xlArray.val.str = L"blahblah";
return static_cast<LPXLOPER12>(&xlArray);
}
double real = arrayin1->val.num;
double imag = arrayin2->val.num;
Complex * z = new Complex(real, imag);
char * handle = StoreObject("Complex", z);
xlArray.xltype = xltypeStr;
FromCharPtrToWChartPtr(handle, &xlArray.val.str);
return static_cast<LPXLOPER12>(&xlArray);
}
__declspec(dllexport) double WINAPI GetComplexNorm(LPXLOPER12 arrayin)
{
char *handle = nullptr;
FromWChartPtrToWharPtr(arrayin->val.str, &handle);
Complex * z = (Complex*)RetrieveObject(handle);
double res = z->getNorm();
return res;
}
Complex
это классический класс комплексных чисел, а функции "память" следующие (остерегайтесь старого кода C):
#include "stdafx.h"
#include <string.h>
#include "memory.h"
#include <stdio.h>
#include "XLCALL.H"
#include <cstdlib>
void FromCharPtrToWChartPtr(char * from, XCHAR **to)
{
size_t len = strlen(from);
*to = new XCHAR[len + 1];
*to[0] = static_cast<XCHAR>(len);
if (len > 0)
{
mbstowcs(*to + 1, from, len + 1);
}
}
void FromWChartPtrToWharPtr(XCHAR * from, char **to)
{
size_t len = from[0];
*to = new char[len + 1];
wcstombs(*to, from + 1, len);
}
typedef struct _TObject
{
char *name;
int version; /* increments by 1 for every new store operation */
void *data;
void *next;
} TObject;
static TObject *cache = NULL;
#define SEPARATOR '#'
TObject* FindNode(char* name)
{
TObject *node = cache;
while (node)
{
if (_stricmp(node->name, name) == 0)
break;
node = (TObject*)node->next;
}
return node;
}
#define FAILURE -1
#define SUCCESS 0
char* StoreObject(char* name, void* data)
{
static char *routine = "StoreObject";
int status = FAILURE;
char *handle = NULL;
TObject *node;
static char buffer[255];
if (data == NULL)
{
// error to handle later
goto done;
}
if (name == NULL)
{
// error to handle later
goto done;
}
if (strlen(name) > 200)
{
// error to handle later
goto done;
}
node = FindNode(name);
if (node == NULL)
{
node = new TObject();
if (node == NULL)
goto done;
node->name = _strdup(name);
node->version = 1;
node->data = data;
node->next = cache;
cache = node;
}
else
{
node->version += 1;
delete node->data; // should I template taylor the object destruction diffenrently ?
node->data = data;
}
sprintf(buffer, "%s%c%d\0", node->name, SEPARATOR, node->version);
handle = _strdup(buffer);
if (handle == NULL)
goto done;
strcpy(handle, buffer);
status = SUCCESS;
done:
if (status != SUCCESS)
{
// error to handle later
delete handle;
handle = NULL;
}
return handle;
}
void* RetrieveObject(char* handle)
{
static char *routine = "RetrieveObject";
int status = FAILURE;
void *data = NULL;
char *name = NULL;
TObject *node;
char *sep;
if (handle == NULL)
{
// error to handle later
goto done;
}
name = _strdup(handle);
if (name == NULL)
goto done;
/* remove version number from lookup string */
sep = strchr(name, SEPARATOR);
if (sep != NULL)
*sep = '\0';
node = FindNode(name);
if (node == NULL)
{
// error to handle later
goto done;
}
data = node->data;
status = SUCCESS;
done:
if (status != SUCCESS)
{
// error to handle later
data = NULL;
}
delete name;
return data;
}
void FreeObjects()
{
TObject *next = cache;
TObject *node;
while (next)
{
node = next;
next = (TObject*)node->next;
delete node->name;
delete node->data;
delete node;
}
}
1 ответ
На этой веб-странице MSN есть хорошая презентация, на самом деле static не является потокобезопасным, вам нужно выделить новую переменную в локальную безопасную память потока. Вы можете взглянуть на функцию get_thread_local_xloper12
на вышеупомянутой странице.