xlAutoFree/xlAutoFree12

Applies to: Excel 2010 | Office 2010 | VBA | Visual Studio

Called by Microsoft Excel just after an XLL worksheet function returns an XLOPER/XLOPER12 to it with a flag set that tells it there is memory that the XLL still needs to release. This enables the XLL to return dynamically allocated arrays, strings, and external references to the worksheet without memory leaks. For more information, see Memory Management in Excel.

Starting in Excel 2007 the xlAutoFree12 function and the XLOPER12 data type are supported.

Excel does not require an XLL to implement and export either of these functions. However, you must do so if your XLL functions return an XLOPER or XLOPER12 that has been dynamically allocated or that contains pointers to dynamically allocated memory. Ensure that your choice of how to manage memory for these types is consistent throughout your XLL and with how you implemented xlAutoFree and xlAutoFree12.

Inside the xlAutoFree/xlAutoFree12 function, callbacks into Excel are disabled, with one exception: xlFree can be called to free Excel-allocated memory.

void WINAPI xlAutoFree(LPXLOPER pxFree);
void WINAPI xlAutoFree12(LPXLOPER12 pxFree);

Parameters

pxFree (LPXLOPER in the case of xlAutoFree)

pxFree (LPXLOPER12 in the case of xlAutoFree12)

A pointer to the XLOPER or the XLOPER12 that has memory that needs to be freed.

Property Value/Return Value

This function does not return a value and should be declared as returning void.

Remarks

When Excel is configured to use multithreaded workbook recalculation, xlAutoFree/xlAutoFree12 is called on the same thread used to call the function that returned it. The call to xlAutoFree/xlAutoFree12 is always made before any subsequent worksheet cells are evaluated on that thread. This simplifies thread-safe design in your XLL.

If the xlAutoFree/xlAutoFree12 function you provide looks at the xltype field of pxFree, remember that the xlbitDLLFree bit will still be set.

Example

Example Implementation 1

The first code from \SAMPLES\EXAMPLE\EXAMPLE.C demonstrates a very specific implementation of xlAutoFree, which is designed to work with just one function, fArray. In general, your XLL will have more than just one function returning memory that needs to be freed, in which case a less restricted implementation is required.

Example implementation 2

The second example implementation is consistent with the assumptions used in the XLOPER12 creation examples in section 1.6.3, xl12_Str_example, xl12_Ref_example, and xl12_Multi_example. The assumptions are that, when the xlbitDLLFree bit has been set, all string, array, and external reference memory has been dynamically allocated using malloc, and so needs to be freed in a call to free.

Example implementation 3

The third example implementation is consistent with an XLL where exported functions that return XLOPER12s allocate strings, external references and arrays using malloc, and where the XLOPER12 itself is also dynamically allocated. Returning a pointer to a dynamically allocated XLOPER12 is one way to ensure that the function is thread safe.

//////////////////////////////////////////
//       BEGIN EXAMPLE IMPLEMENTATION 1
//////////////////////////////////////////

LPXLOPER12 WINAPI fArray(void)
{
    LPXLOPER12 pxArray;
    static XLOPER12 xMulti;
    int i;
    int rwcol;

    xMulti.xltype = xltypeMulti | xlbitDLLFree;
    xMulti.val.array.columns = 1;
    xMulti.val.array.rows = 8;

    // For large values of rows and columns, this would overflow
    // use __int64 in that case and return an error if rwcol
    // contains a number that won't fit in sizeof(int) bytes

    rwcol = xMulti.val.array.columns * xMulti.val.array.rows; 

    pxArray = (LPXLOPER12)GlobalLock(hArray = GlobalAlloc(GMEM_ZEROINIT, rwcol * sizeof(XLOPER12)));

    xMulti.val.array.lparray = pxArray;

    for(i = 0; i < rwcol; i++) 
    {
        pxArray[i].xltype = xltypeInt;
        pxArray[i].val.w = i;
    }

// Word of caution - returning static XLOPERs/XLOPER12s is not thread safe
// for UDFs declared as thread safe, use alternate memory allocation mechanisms

    return (LPXLOPER12)&xMulti;
}

void WINAPI xlAutoFree12(LPXLOPER12 pxFree)
{
    GlobalUnlock(hArray);
    GlobalFree(hArray);
    return;
}

//////////////////////////////////////////
//       BEGIN EXAMPLE IMPLEMENTATION 2
//////////////////////////////////////////
void WINAPI xlAutoFree12(LPXLOPER12 pxFree)
{
    if(pxFree->xltype & xltypeMulti)
    {
/* Assume all string elements were allocated using malloc, and
** need to be freed using free.  Then free the array itself.
*/
        int size = pxFree->val.array.rows *
            pxFree->val.array.columns;
        LPXLOPER12 p = pxFree->val.array.lparray;
        for(; size-- > 0; p++) // check elements for strings
            if(p->xltype == xltypeStr)
                free(p->val.str);
        free(pxFree->val.array.lparray);
    }
    else if(pxFree->xltype & xltypeStr)
    {
        free(pxFree->val.str);
    }
    else if(pxFree->xltype & xltypeRef)
    {
        free(pxFree->val.mref.lpmref);
    }
}

//////////////////////////////////////////
//       BEGIN EXAMPLE IMPLEMENTATION 3
//////////////////////////////////////////
LPXLOPER12 WINAPI example_xll_function(LPXLOPER12 pxArg)
{
// Thread-safe return value. Every invocation of this function
// gets its own piece of memory.
    LPXLOPER12 pxRtnValue = (LPXLOPER12)malloc(sizeof(XLOPER12));

// Initialize to a safe default
    pxRtnValue->xltype = xltypeNil;

// Set the value of pxRtnValue making sure that strings, external
// references, arrays, and strings within arrays are all dynamically
// allocated using malloc.
//    (code omitted)
//    ...

// Set xlbitDLLFree regardless of the type of the return value to
// ensure xlAutoFree12 is called and pxRtnValue is freed.
    pxRtnValue->xltype |= xlbitDLLFree;
    return pxRtnValue;
}

void WINAPI xlAutoFree12(LPXLOPER pxFree)
{
    if(pxFree->xltype & xltypeMulti)
    {
// Assume all string elements were allocated using malloc, and
// need to be freed using free. Then free the array itself.
        int size = pxFree->val.array.rows *
            pxFree->val.array.columns;
        LPXLOPER12 p = pxFree->val.array.lparray;

        for(; size-- > 0; p++) // check elements for strings
            if(p->xltype == xltypeStr)
                free(p->val.str);

        free(pxFree->val.array.lparray);
    }
    else if(pxFree->xltype & xltypeStr)
    {
        free(pxFree->val.str);
    }
    else if(pxFree->xltype & xltypeRef)
    {
        free(pxFree->val.mref.lpmref);
    }
// Assume pxFree was itself dynamically allocated using malloc.
    free(pxFree);
}

See Also

Concepts

Add-in Manager and XLL Interface Functions