Gerenciamento de Memória no Excel
Aplica-se a: Excel 2013 | Office 2013 | Visual Studio
A gestão da memória é a preocupação mais importante se quiser criar XLLs eficientes e estáveis. A falha na gestão da memória pode levar a uma série de problemas no Microsoft Excel, desde pequenos problemas, como a alocação e inicialização de memória ineficientes e pequenas fugas de memória, até problemas importantes, como a desestabilização do Excel.
A má gestão da memória é a fonte mais comum de problemas graves relacionados com suplementos. Por conseguinte, deve criar o seu projeto com uma estratégia consistente e bem pensada na gestão da memória.
A gestão de memória tornou-se mais complexa no Microsoft Office Excel 2007 com a introdução do recálculo de livros multithreaded. Se quiser criar e exportar funções de folha de cálculo seguras para threads, tem de gerir os conflitos resultantes que podem ocorrer quando vários threads competem pelo acesso.
Existem considerações de memória para os três tipos de estrutura de dados seguintes:
- XLOPERs e XLOPER12s
- Cadeias que não estão num XLOPER ou XLOPER12
- Matrizes FP e FP12
Memória de XLOPER/XLOPER12
A estrutura de dados XLOPER/ XLOPER12 tem alguns subtipos que contêm ponteiros para blocos de memória, nomeadamente cadeias (xltypeStr), matrizes (xltypeMulti) e referências externas (xltypeRef). Tenha também em atenção que as matrizes xltypeMulti podem conter a cadeia XLOPER/ XLOPER12s que, por sua vez, apontam para outros blocos de memória.
UmaXLOPER12XLOPER/ pode ser criada de várias formas:
- Pelo Excel ao preparar argumentos para serem transmitidos para uma função XLL
- Pelo Excel ao devolver um XLOPER ou XLOPER12 numa chamada à API C
- Pela DLL ao criar argumentos para serem transmitidos para uma chamada à API C
- Pela DLL ao criar um valor de retorno da função XLL
Um bloco de memória dentro de um dos tipos que apontam memória pode ser alocado de várias formas:
- Pode ser um bloco estático dentro da DLL fora de qualquer código de função, caso em que não tem de alocar ou libertar a memória.
- Pode ser um bloco estático na DLL dentro de algum código de função, caso em que não tem de alocar ou libertar a memória.
- Pode ser alocado e libertado dinamicamente pela sua DLL de várias formas possíveis: malloc e gratuito, novo e eliminado, etc.
- Pode ser alocado dinamicamente pelo Excel.
Tendo em conta o número de origens possíveis para o XLOPER/ XLOPER12 memória e o número de situações em que o XLOPER/ XLOPER12 poderia ter tido essa memória atribuída, não é de estranhar que este assunto possa parecer muito difícil. No entanto, a complexidade pode ser muito reduzida se seguir várias regras e diretrizes.
Regras para Trabalhar com XLOPER/XLOPER12
Não tente libertar memória nem substituir XLOPERs/ XLOPER12s que são transmitidos como argumentos para a função XLL. Deve tratar esses argumentos como só de leitura. Para obter mais informações, consulte "Devolver XLOPER ou XLOPER12 Ao Modificar Argumentos no Local" em Problemas Conhecidos no Desenvolvimento XLL do Excel.
Se o Excel tiver alocado memória para um XLOPER/ XLOPER12 devolvido à DLL numa chamada à API C:
- Tem de libertar a memória quando já não precisar do XLOPER/ XLOPER12 através de uma chamada para xlFree. Não utilize qualquer outro método, como libertar ou eliminar, para libertar a memória.
- Se o tipo devolvido for xltypeMulti, não substitua quaisquerXLOPER12XLOPER/ na matriz, especialmente se contiverem cadeias e, especialmente, não se estiver a tentar substituir por uma cadeia.
- Se quiser devolver o XLOPER/ XLOPER12 ao Excel como o valor devolvido da sua função DLL, tem de indicar ao Excel que existe memória que o Excel tem de libertar quando terminar.
Só tem de chamar xlFree numaXLOPER12XLOPER/ que tenha sido criada como o valor devolvido para uma chamada à API C.
Se a DLL tiver alocado memória a um XLOPER/ XLOPER12 que pretende voltar ao Excel como o valor devolvido da sua função DLL, tem de indicar ao Excel que existe memória que a DLL tem de libertar.
Diretrizes para a Gestão de Memória
- Seja consistente na DLL no método que utiliza para alocar e libertar memória. Evite misturar métodos. Uma boa abordagem consiste em encapsular o método que está a utilizar numa classe ou estrutura de memória, na qual pode alterar o método utilizado sem alterar o código em muitos locais.
- Quando cria matrizes xltypeMulti na DLL, seja consistente na forma como aloca memória para cadeias: aloque sempre dinamicamente a memória para as mesmas ou utilize sempre memória estática. Se o fizer, quando estiver a libertar a memória, saberá que tem de libertar sempre ou nunca as cadeias de carateres.
- Faça cópias profundas da memória alocada ao Excel quando estiver a copiar umaXLOPER12XLOPER/ criada pelo Excel.
- Não coloque a cadeia de carateres Alocada pelo Excel XLOPER/ XLOPER12s dentro de matrizes xltypeMulti . Faça cópias profundas das cadeias e armazene ponteiros para as cópias na matriz.
Libertar memória Excel-Allocated XLOPER/XLOPER12
Considere o seguinte comando XLL, que utiliza xlGetName para obter uma cadeia que contém o caminho e o nome de ficheiro da DLL e o apresenta numa caixa de diálogo de alerta com xlcAlert.
int WINAPI show_DLL_name(void)
{
XLOPER12 xDllName;
if(Excel12(xlfGetName, &xDllName, 0) == xlretSuccess)
{
// Display the name.
Excel12(xlcAlert, 0, 1, &xDllName);
// Free the memory that Excel allocated for the string.
Excel12(xlFree, 0, 1, &xDllName);
}
return 1;
}
Quando a função já não precisa da memória apontada por xDllName, pode libertá-la através de uma chamada para a função xlFree, uma das funções da API C apenas DLL.
A função xlFree está totalmente documentada na secção de referência da função (veja Funções da API C Que Só Podem Ser Chamadas a partir de uma DLL ou XLL), mas tenha em atenção o seguinte:
- Pode passar ponteiros para mais do que um XLOPER/ XLOPER12numa única chamada para xlFree, limitado apenas pelo número de argumentos de função suportados na versão em execução do Excel (30 no Excel 2003, 255 a partir do Excel 2007).
- xlFree define o ponteiro contido como NULL para garantir que uma tentativa de libertar umaXLOPER12XLOPER/ que já foi libertada é segura. xlFree é a única função da API C que modifica os respetivos argumentos.
- Pode chamar xlFree em segurança em qualquer XLOPER12 XLOPER/ utilizado para o valor devolvido de uma chamada à API C, independentemente de conter um ponteiro para a memória.
Devolver XLOPER/XLOPER12s para ser libertado pelo Excel
Suponha que queria modificar o comando de exemplo na secção anterior e alterá-lo para uma função de folha de cálculo que devolve o caminho DLL e o nome do ficheiro quando é transmitido um argumento Booleanoverdadeiro e, caso contrário, #N/D . É evidente que não pode chamar xlFree para libertar a memória da cadeia antes de a devolver ao Excel. No entanto, se não for libertado em algum momento, o suplemento irá perder memória sempre que a função for chamada. Para contornar este problema, pode definir um pouco no campo xltype doXLOPER12XLOPER/ , definido como xlbitXLFree em xlcall.h. A definição indica ao Excel que tem de libertar a memória devolvida quando terminar de copiar o valor.
Exemplo
O seguinte exemplo de código mostra o comando XLL na secção anterior convertido numa função de folha de cálculo XLL.
LPXLOPER12 WINAPI get_DLL_name(int calculation_trigger)
{
static XLOPER12 xRtnValue; // Not thread-safe
Excel12(xlfGetName, &xRtnValue, 0);
// If xlfGetName failed, xRtnValue will be #VALUE!
if(xRtnValue.xltype == xltypeStr)
{
// Tell Excel to free the string memory after
// it has copied out the return value.
xRtnValue.xltype |= xlbitXLFree;
}
return &xRtnValue;
}
As funções XLL que utilizam XLOPER/ XLOPER12s têm de ser declaradas como tomando e devolvendo ponteiros para XLOPER/ XLOPER12s. A utilização neste exemplo de uma XLOPER12 estática dentro da função não é segura para threads. Pode registar incorretamente esta função como segura para threads, mas arriscaria que xRtnValue fosse substituído por um thread antes de outro thread ter terminado.
Tem de definir xlbitXLFree após a chamada para a chamada de retorno do Excel que a aloca. Se o definir antes disso, este é substituído e não tem o efeito pretendido. Se pretender utilizar o valor como argumento numa chamada a outra função da API C antes de o devolver à folha de cálculo, deve definir este bit após qualquer chamada. Caso contrário, irá confundir funções que não mascaram este bit antes de verificar o tipode XLLOPER12XLOPER/ .
Devolver XLOPER/XLOPER12s para ser libertado pela DLL
Um problema semelhante a este ocorre quando o XLL atribuiu memória a umaXLOPER12XLOPER/ e quer devolvê-la ao Excel. O Excel reconhece outro bit que pode ser definido no campo xltype doXLOPER12XLOPER/ , definido como xlbitDLLFree em xlcall.h.
Quando o Excel recebe umaXLOPER12XLOPER/ com este conjunto de bits, tenta chamar uma função que deve ser exportada pelo XLL denominado xlAutoFree (para XLOPERs) ou xlAutoFree12 (para XLOPER12s). Esta função é descrita mais detalhadamente na referência de função (veja Gestor de Suplementos e Funções de Interface XLL), mas é dada uma implementação mínima de exemplo aqui. O seu objetivo é libertar o XLOPER/ XLOPER12 memória de uma forma consistente com a forma como foi originalmente alocado.
Exemplos
A função de exemplo seguinte faz o mesmo que a função anterior, exceto que inclui o texto "O nome do caminho completo para esta DLL é " antes do nome DLL.
#include <string.h>
LPXLOPER12 WINAPI get_DLL_name_2(int calculation_trigger)
{
static XLOPER12 xRtnValue; // Not thread-safe
Excel12(xlfGetName, &xRtnValue, 0);
// If xlfGetName failed, xRtnValue will be #VALUE!
if(xRtnValue.xltype != xltypeStr)
return &xRtnValue;
// Make a copy of the DLL path and file name.
wchar_t *leader = L"The full pathname for this DLL is ";
size_t leader_len = wcslen(leader);
size_t dllname_len = xRtnValue.val.str[0];
size_t msg_len = leader_len + dllname_len;
wchar_t *msg_text = (wchar_t *)malloc(msg_len + 1);
wcsncpy_s(msg_text + 1, leader, leader_len);
wcsncpy_s(msg_text + 1 + leader_len, xRtnValue.val.str + 1,
dllname_len);
msg_text[0] = msg_len;
// Now the original string has been copied Excel can free it.
Excel12(xlFree, 0, 1, &xRtnValue);
// Now reuse the XLOPER12 for the new string.
xRtnValue.val.str = msg_text;
// Tell Excel to call back into the DLL to free the string
// memory after it has copied out the return value.
xRtnValue.xltype = xltypeStr | xlbitDLLFree;
return &xRtnValue;
}
Uma implementação minimamente suficiente de xlAutoFree12 no XLL que exportou a função anterior seria a seguinte.
void WINAPI xlAutoFree12(LPXLOPER12 p_oper)
{
if(p_oper->xltype == (xltypeStr | xlbitDLLFree))
free(p_oper->val.str);
}
Esta implementação só é suficiente se o XLL devolver apenas XLOPER12 cadeias de carateres e essas cadeias só forem alocadas com malloc. Tenha em atenção que o teste
if(p_oper->xltype == xltypeStr)
neste caso, ocorrerá uma falha porque xlbitDLLFree está definido.
Em geral, xlAutoFree e xlAutoFree12 devem ser implementados para que libertem memória associada a matrizes xltypeMulti criadas por XLL e referências externas xltypeRef .
Pode decidir implementar as suas funções XLL para que TODAS devolvam XLOPERse XLOPER12s alocados dinamicamente. Neste caso, teria de definir xlbitDLLFree em todos esses XLOPERse XLOPER12s, independentemente do subtipo. Também teria de implementar xlAutoFree e xlAutoFree12 para que esta memória fosse libertada e também qualquer memória apontada para dentro daXLOPER12XLOPER/ . Esta abordagem é uma forma de tornar o thread de valor devolvido seguro. Por exemplo, a função anterior pode ser reescrita da seguinte forma.
#include <string.h>
LPXLOPER12 WINAPI get_DLL_name_3(int calculation_trigger)
{
// Thread-safe
LPXLOPER12 pxRtnValue = (LPXLOPER12)malloc(sizeof(XLOPER12));
Excel12(xlfGetName, pxRtnValue, 0);
// If xlfGetName failed, pxRtnValue will be #VALUE!
if(pxRtnValue->xltype != xltypeStr)
{
// Even though an error type does not point to memory,
// Excel needs to pass this oper to xlAutoFree12 to
// free pxRtnValue itself.
pxRtnValue->xltype |= xlbitDLLFree;
return pxRtnValue;
}
// Make a copy of the DLL path and file name.
wchar_t *leader = L"The full pathname for this DLL is ";
size_t leader_len = wcslen(leader);
size_t dllname_len = pxRtnValue->val.str[0];
size_t msg_len = leader_len + dllname_len;
wchar_t *msg_text = (wchar_t *)malloc(msg_len + 1);
wcsncpy_s(msg_text + 1, leader, leader_len);
wcsncpy_s(msg_text + 1 + leader_len, pxRtnValue->val.str + 1,
dllname_len);
msg_text[0] = msg_len;
// Now the original string has been copied Excel can free it.
Excel12(xlFree, 0, 1, pxRtnValue);
// Now reuse the XLOPER12 for the new string.
pxRtnValue->val.str = msg_text;
pxRtnValue->xltype = xltypeStr | xlbitDLLFree;
return pxRtnValue;
}
void WINAPI xlAutoFree12(LPXLOPER12 p_oper)
{
if(p_oper->xltype == (xltypeStr | xlbitDLLFree))
free(p_oper->val.str);
free(p_oper);
}
Para obter mais informações sobre xlAutoFree e xlAutoFree12, consulte xlAutoFree/xlAutoFree12.
Devolver Argumentos de Modificação no Local
O Excel permite que uma função XLL devolva um valor ao modificar um argumento no local. Só pode fazê-lo com um argumento transmitido como um ponteiro. Para funcionar desta forma, a função tem de ser registada de forma a indicar ao Excel que argumento será modificado.
Este método de devolução de um valor é suportado para todos os tipos de dados que podem ser transmitidos por ponteiro, mas é especialmente útil para os seguintes tipos:
- Cadeias de bytes ASCII com contagem de comprimento e terminadas nulos
- Cadeias de carateres largas Unicode com contagem de comprimento e terminadas nulos (a partir do Excel 2007)
- Matrizes de vírgula flutuante FP
- Matrizes de vírgula flutuante FP12 (a partir do Excel 2007)
Observação
Não deve tentar devolver XLOPERsou XLOPER12s desta forma. Confira mais informações em Problemas conhecidos no desenvolvimento de XLL do Excel.
A vantagem de utilizar esta técnica, em vez de utilizar apenas a instrução return, é que o Excel aloca a memória para os valores devolvidos. Assim que o Excel terminar de ler os dados devolvidos, liberta a memória. Esta ação afasta as tarefas de gestão de memória da função XLL. Esta técnica é segura para threads: se for chamada simultaneamente pelo Excel em threads diferentes, cada chamada de função em cada thread tem a sua própria memória intermédia.
É útil para os tipos de dados listados anteriormente, em particular, porque o mecanismo de chamada de volta para a DLL para libertar memória após a devolução que existe para XLOPER/ XLOPER12s não existe para cadeias simples e matrizes FP/ FP12 . Por conseguinte, ao devolver uma cadeia criada por DLL ou uma matriz de vírgula flutuante, tem as seguintes opções:
- Defina um ponteiro persistente para uma memória intermédia alocada dinamicamente e devolva o ponteiro. Na chamada seguinte para a função (1), verifique se o ponteiro não é nulo, (2) liberte os recursos alocados na chamada anterior e reponha o ponteiro para nulo, (3) reutilize o ponteiro para um bloco de memória recentemente alocado.
- Crie as suas cadeias e matrizes numa memória intermédia estática que não precisa de ser libertada e devolva um ponteiro a esse ponto.
- Modifique um argumento no local, escrevendo a sua cadeia ou matriz diretamente no espaço reservado pelo Excel.
Caso contrário, tem de criar umaXLOPER12XLOPER/ e utilizar xlbitDLLFree e xlAutoFree/ xlAutoFree12 para libertar os recursos.
A última opção pode ser a mais simples nos casos em que lhe está a ser transmitido um argumento do mesmo tipo que o valor devolvido. O ponto-chave a ter em atenção é que os tamanhos da memória intermédia são limitados e tem de ter muito cuidado para não os ultrapassar. Se errar, o Excel pode falhar. Os tamanhos da memória intermédia para cadeias de carateres e matrizes FP/ FP12 são abordados a seguir.
Cadeias de caracteres
Os problemas com a gestão da memória de cadeia são, sem dúvida, a causa mais comum de instabilidade nas aplicações e suplementos. Talvez seja compreensível, tendo em conta a variedade de formas pelas quais as cadeias de carateres são processadas: terminadas nulas ou com contagem de comprimentos (ou ambas); memórias intermédias estáticas ou dinâmicas; comprimento fixo ou comprimento quase ilimitado; memória gerida do sistema operativo (por exemplo, OLE Bstr) ou cadeias não geridas; e assim sucessivamente.
Os programadores C/C++ estão mais familiarizados com as cadeias terminadas por nulos. A biblioteca C padrão foi concebida para funcionar com essas cadeias. Os literais de cadeia estático no código são compilados em cadeias terminadas nulas. Em alternativa, o Excel funciona com cadeias de comprimento contados que não são terminadas de forma geral nula. A combinação destes factos requer uma abordagem clara e consistente na DLL/XLL relativamente à forma como processa as cadeias de carateres e a memória de cadeia.
Os problemas mais comuns são os seguintes:
- A passagem de um ponteiro nulo ou inválido para uma função que espera um ponteiro válido e não verifica ou não consegue verificar a validade do ponteiro em si.
- A ultrapassagem dos limites de uma memória intermédia de cadeia por uma função que não verifica ou não o comprimento da memória intermédia em relação ao comprimento da cadeia que está a ser escrita.
- Tentar libertar memória intermédia de cadeia que é estática ou já foi libertada ou que não foi alocada de uma forma consistente com a forma como está a ser libertada.
- Fugas de memória que resultam da alocação de cadeias e, em seguida, não são libertadas, normalmente numa função frequentemente chamada.
Regras para Cadeias de Carateres
Tal como acontece com XLOPER/ XLOPERs, existem regras e diretrizes que deve seguir. As diretrizes são as mesmas que foram dadas na secção anterior. As regras aqui são uma extensão das regras especificamente para cadeias de carateres.
Regras:
- Não tente libertar memória ou substituir a cadeia de carateres XLOPER/ XLOPER12s ou cadeias simples com contagem de comprimento ou terminadas nulas transmitidas como argumentos para a função XLL. Deve tratar esses argumentos como só de leitura.
- Quando o Excel aloca memória para uma cadeia decarateresXLOPER/ XLOPER12 para o valor devolvido de uma função de chamada de retorno da API C, utilize xlFree para a libertar ou defina xlbitXLFree se o devolver ao Excel a partir de uma função XLL.
- Quando a DLL aloca dinamicamente uma memória intermédia de cadeia para umXLOPER12XLOPER/ , liberte-a de uma forma consistente com a forma como a alocou quando terminar, ou defina xlbitDLLFree se a devolver ao Excel a partir de uma função XLL e, em seguida, liberte-a no xlAutoFree/ xlAutoFree12.
- Se o Excel tiver alocado memória para uma matriz xltypeMulti devolvida à DLL numa chamada à API C, não substitua nenhuma cadeia de carateres XLOPER/ XLOPER12s na matriz. Essas matrizes só têm de ser libertadas com xlFree ou, se forem devolvidas por uma função XLL, ao definir xlbitXLFree.
Tipos de Cadeia Suportados
C API xltypeStr XLOPER/XLOPER12s
Cadeias de bytes: XLOPER | Cadeias de carateres largas: XLOPER12 |
---|---|
Todas as versões do Excel | Começando no Excel 2007 |
Comprimento máximo: 255 bytes ASCII expandidos | Comprimento máximo de 32 767 carateres Unicode |
Primeiro byte (não assinado) = comprimento | Primeiro caráter Unicode = comprimento |
Importante
Não assuma a terminação nula de cadeias XLOPER ou XLOPER12 .
Cadeias C/C++
Cadeias de bytes | Cadeias de carateres largas |
---|---|
Comprimento máximo de "C" terminado nulo (caráter *): 255 bytes ASCII expandidos | Null-terminated (wchar_t *) "C%" Maximum length 32.767 Unicode chars |
Contagem de comprimentos (caráter não assinado *) "D" | Contagem de comprimentos (wchar_t *) "D%" |
Cadeias em matrizes xltypeMulti XLOPER/XLOPER12
Em vários casos, o Excel cria uma matriz xltypeMulti para utilização na sua DLL/XLL. Várias das funções de informação XLM devolvem essas matrizes. Por exemplo, a função de API C xlfGetWorkspace, quando transmitida o argumento 44, devolve uma matriz que contém cadeias que descrevem todos os procedimentos de DLL atualmente registados. A função da API C xlfDialogBox devolve uma cópia modificada do respetivo argumento de matriz, que contém cópias profundas das cadeias. Talvez a forma mais comum de um XLL encontrar uma matriz xltypeMulti é onde foi transmitida como um argumento para uma função XLL ou foi coagida a este tipo a partir de uma referência de intervalo. Nestes últimos casos, o Excel cria cópias profundas das cadeias nas células de origem e aponta para estas dentro da matriz.
Onde pretende modificar estas cadeias de carateres na DLL, deve fazer as suas próprias cópias profundas. Quando estiver a criar as suas próprias matrizes xltypeMulti, não deve colocar a cadeia de carateres XLOPER/ alocada pelo ExcelXLOPER12s dentro das mesmas. Isto arrisca-se a que não os liberte corretamente mais tarde ou não os liberte de todo. Mais uma vez, deve fazer cópias profundas das cadeias e armazenar ponteiros para as cópias na matriz.
Exemplos
A função de exemplo seguinte cria uma cópia alocada dinamicamente de uma cadeia Unicode com contagem de comprimento. Tenha em atenção que o autor da chamada deve, em última análise, libertar a memória alocada neste exemplo através da eliminação[], e que a cadeia de origem não é assumida como nula terminada. A cadeia de cópia é truncada, se necessário, por motivos de segurança, e não é terminada de forma nula.
#include <string.h>
#define MAX_V12_STRBUFFLEN 32678
wchar_t * deep_copy_wcs(const wchar_t *p_source)
{
if(!p_source)
return NULL;
size_t source_len = p_source[0];
bool truncated = false;
if(source_len >= MAX_V12_STRBUFFLEN)
{
source_len = MAX_V12_STRBUFFLEN - 1; // Truncate the copy
truncated = true;
}
wchar_t *p_copy = new wchar_t[source_len + 1];
wcsncpy_s(p_copy, p_source, source_len + 1);
if(truncated)
p_copy[0] = source_len;
return p_copy;
}
Em seguida, esta função pode ser utilizada com segurança para copiar um XLOPER12, conforme mostrado na seguinte função XLL exportável que devolve uma cópia do respetivo argumento se for uma cadeia. Todos os outros tipos são devolvidos como uma cadeia de comprimento zero. Tenha em atenção que os intervalos não são processados— a função devolve #VALUE!. A função tem de ser registada como tendo um argumento do tipo U para que as referências sejam transmitidas como valores. Isto é equivalente à função de folha de cálculo incorporada T(), exceto que AsText também converte erros em cadeias de comprimento zero. Este exemplo de código pressupõe que xlAutoFree12 liberta o ponteiro de entrada e também o respetivo conteúdo através da eliminação.
LPXLOPER12 WINAPI AsText(LPXLOPER12 pArg)
{
LPXLOPER12 pRtnVal = new XLOPER12;
// If the input was an array, only operate on the top-left element.
LPXLOPER *pTemp;
if(pArg->xltype == xltypeMulti)
pTemp = pArg->val.array.lparray;
else
pTemp = pArg;
switch(pTemp->xltype)
{
case xltypeErr:
case xltypeNum:
case xltypeMissing:
case xltypeNil:
case xltypeBool:
pRtnVal->xltype = xltypeStr | xlbitDLLFree;
pRtnVal->val.str = deep_copy_wcs(L"\000");
return pRtnVal;
case xltypeStr:
pRtnVal->xltype = xltypeStr | xlbitDLLFree;
pRtnVal->val.str = deep_copy_wcs(pTemp->val.str);
return pRtnVal;
default: // xltypeSRef, xltypeRef, xltypeFlow, xltypeInt
pRtnVal->xltype = xltypeErr | xlbitDLLFree;
pRtnVal->val.err = xlerrValue;
return pRtnVal;
}
}
Devolver Argumentos de Cadeia de Modificação no Local
Os argumentos registados como tipos F, G, F%e G% podem ser modificados no local. Quando o Excel está a preparar argumentos de cadeia para estes tipos, cria uma memória intermédia de comprimento máximo. Em seguida, copia a cadeia de argumento para essa cadeia, mesmo que esta cadeia seja muito mais curta. Isto permite que a função XLL escreva o respetivo valor devolvido diretamente na mesma memória.
Os tamanhos da memória intermédia separados para estes tipos são os seguintes:
- Cadeias de bytes: 256 bytes, incluindo o contador de comprimento (tipo G) ou a terminação nula (tipo F).
- Cadeias Unicode: 32.768 carateres largos (65 536 bytes), incluindo o contador de comprimento (tipo G%) ou a terminação nula (tipo F%).
Observação
Não pode chamar essa função diretamente a partir do Visual Basic for Applications (VBA), porque não pode garantir que foi alocada uma memória intermédia suficientemente grande. Só pode chamar essa função de outra DLL com segurança se tiver transmitido explicitamente uma memória intermédia suficientemente grande.
Eis um exemplo de uma função XLL que inverte uma cadeia de carateres largos com terminação nula transmitida com a função de biblioteca padrão wcsrev. Neste caso, o argumento seria registado como tipo F%.
void WINAPI reverse_text_xl12(wchar_t *text)
{
_wcsrev(text);
}
Armazenamento Persistente (Nomes Binários)
Os nomes binários são definidos e associados a blocos de dados binários, ou seja, não estruturados, armazenados em conjunto com o livro. São criados com a função xlDefineBinaryName e os dados são obtidos com a função xlGetBinaryName. Ambas as funções são descritas mais detalhadamente na referência da função (veja Funções da API C Que Só Podem Ser Chamadas a partir de uma DLL ou XLL) e ambas utilizam o XLOPER/ xltypeBigDataXLOPER12.
Para obter informações sobre problemas conhecidos que limitam as aplicações práticas de nomes binários, veja Problemas Conhecidos no Desenvolvimento XLL do Excel.
Pilha do Excel
O Excel partilha o respetivo espaço de pilha com todos os DLLs que carregou. Normalmente, o espaço em pilha é mais do que adequado para utilização comum e não precisa de se preocupar com isso, desde que siga algumas diretrizes:
- Não transmita estruturas muito grandes como argumentos para funções por valor na pilha. Em alternativa, transmita ponteiros ou referências.
- Não devolva estruturas grandes na pilha. Devolver ponteiros à memória estática ou alocada dinamicamente ou utilizar argumentos transmitidos por referência.
- Não declare estruturas variáveis automáticas muito grandes no código da função. Se precisar deles, declare-os como estáticos.
- Não chame funções recursivamente, a menos que tenha a certeza de que a profundidade da recursão será sempre superficial. Em alternativa, experimente utilizar um ciclo.
Quando uma DLL chama de volta para o Excel através da API C, o Excel verifica primeiro se existe espaço suficiente na pilha para a chamada de utilização na pior das hipóteses que poderia ser feita. Se pensa que pode não haver espaço suficiente, falhará a chamada para ser segura, mesmo que possa ter havido espaço suficiente para essa chamada em particular. Neste caso, as chamadas de retorno devolvem o código xlretFailed. Para uma utilização comum da API C e da pilha, esta é uma causa improvável da falha de uma chamada à API C.
Se estiver preocupado, apenas curioso ou quiser eliminar a falta de espaço em pilha como motivo para uma falha inexplicável, pode descobrir quanto espaço de pilha existe com uma chamada para a função xlStack .
Confira também
Recálculo com vários threads no Excel
Multithreading and Memory Contention in Excel
Desenvolvimento de XLLs do Excel