0% found this document useful (0 votes)
321 views37 pages

Developing Add-Ins (XLLS) in Excel 2007

Developing Add-Ins (XLLs) in Excel 2007

Uploaded by

siswout
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
321 views37 pages

Developing Add-Ins (XLLS) in Excel 2007

Developing Add-Ins (XLLs) in Excel 2007

Uploaded by

siswout
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 37

4/5/2017 DevelopingAddins(XLLs)inExcel2007

This documentation is archived and is not being maintained.

Developing Addins XLLs in Excel 2007


Office 2007

Summary: Learn about Microsoft Office Excel 2007 features that affect XLL addins and enable new XLL functionality, and also changes to the XLL C API itself. 23 printed
pages

Steve Dalton, Eigensys Ltd.

October 2006

Updated: September 2007

Applies to: 2007 Microsoft Office System, Microsoft Office Excel 2007

Contents

Developing XLLs in Excel 2007

Overview of Excel 2007 XLLRelated Features

Overview of XLLs

Changes to XLLs in Excel 2007

Writing CrossVersion XLLs

Writing ThreadSafe XLLs and Worksheet Functions

Conclusion

Additional Resources

Download the Excel 2007 XLL Software Development Kit.


https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 1/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

Developing XLLsis in
This documentation Excel
archived 2007
and is not being maintained.
The intended audience for this article consists of C and C++ developers who already have experience developing Microsoft Office Excel addins, or XLLs. This article is
not an introduction to XLL development although a brief overview is included. To make the most of this article, readers should be familiar with:

C and C++ language concepts and constructs. Code examples are written in C++.

Building DLLs that export functions.

The XLOPER data structure and other Excel data types, such as the floatingpoint matrix structure FP.

The addin manager interface functions, such as xlAutoOpen and xlAutoClose.

XLOPER memory management xlAutoFree, xlFree, and the use of xlbitDLLFree and xlbitXLFree.

Important

The functionality described in this article is now available in the Microsoft Office Excel 2007 XLL Software Development Kit SDK. The Excel 2007 XLL SDK is
available as a download from the Microsoft Download Center.

Overview of Excel 2007 XLL-Related Features


The most obvious change to XLLrelated features in Microsoft Office Excel 2007 is that the worksheet is expanded from 224 to 234 cells, or to be more precise, from 256
columns x 65,536 rows 28 x 216 to 16,384 x 1,048,576 214 x 220. These new limits overflow the integer types that contain them in the old range and array structures.
This change requires new data structures with wider integers to define the size of ranges and arrays.

The maximum number of arguments a function can take increases from 30 to 255. Additionally, XLLs can now exchange long Unicode strings with Excel instead of just
limitedlength byte strings.

Multithreaded workbook recalculation is supported on singleprocessor and multiprocessor computers. Unlike Microsoft Visual Basic for Applications VBA User
Defined Functions UDFs, you can register XLL UDFs as threadsafe. Like the majority of the builtin worksheet functions in Excel, you can assign them to concurrent
threads to accelerate recalculation. This benefit comes at the expense of some restrictions, and also a responsibility not to abuse the multithreading privileges with
unsafe behavior.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 2/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

The Analysis Toolpak functions are now fully integrated into Excel, although the addin is still required for the Data Analysis tools. This creates an area of incompatibility
for XLLs developed for earlier versions that call ATP functions using xlUDF.
This documentation is archived and is not being maintained.
The user interface also changes dramatically. Customization of the user interface is beyond the scope of this article, other than to say that old XLL custom menus and
menu items are still enabled but are not placed where the old C application programming interface API functions intended.

Overview of XLLs
Since Microsoft Excel 4.0, the linking of XLLs to Excel has been supported. In addition, an interface known as the C API, through which the XLL can access Excel
functions and commands, is also supported. XLL is the name for a DLL that contains the callbacks required by the Excel Addin Manager, and also the XLL's exported
commands and worksheet functions. This interface has not changed significantly since Excel 5.0. Many of the new features of Excel 2007, and some features of earlier
versions that were not previously supported, are now available in an updated C API. This article reviews additions to the new C API and discusses some transition issues
that developers face.

Microsoft published a software development kit SDK with Excel 97, including the following components:

A C header file, xlcall.h, containing definitions of data structures used by Excel to exchange data with XLLs; enumerated function and command definitions
corresponding to the builtin Excel worksheet functions, XLM information functions and many commands; and prototypes for the Excel callback functions Excel4,
Excel4v, and XLCallVer.

A static import library, xlcall32.lib. The Excel callbacks are exported from this library with C name decoration.

An XLL SDK Framework project containing a complete XLL project and a number of routines for handling Excel data types and assisting with calls to Excel4 and
Excel4v.

The new version of the Excel 2007 XLL SDK is available on the Microsoft Download Center.

The simplest XLL is one that exports a function called by Excel when the addin is loaded: xlAutoOpen. This function performs initialization tasks and registers any
functions and commands the XLL exports. This entails the addin calling the C API equivalent of the XLM function REGISTER. An Excel library, xlcall32.dll, exports
functions that enable XLLs to call back into Excel: Excel4 and Excel4v reflecting that it was version 4 when XLL functionality was introduced, and now supplemented by
Excel12 and Excel12v in Excel 2007.

The Excel Addin Manager loads and manages XLLs. It looks for the following XLL exports:

xlAutoOpen: Called when the XLL is loaded. The ideal place to register XLL functions and commands, initialize data structures, and customize the user interface.

xlAutoClose: Called when the XLL is unloaded. The place to unregister functions and commands, release resources, and undo customizations.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 3/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xlAutoAdd: Called when the XLL is activated or loaded during a session.

xlAutoRemove: Called when the XLL is inactivated or unloaded during a session.


This documentation is archived and is not being maintained.
xlAddInManagerInfo xlAddInManagerInfo12: Called when the addin manager is invoked for the first time. If passed an argument = 1, it returns a string the
name of the addin, otherwise it should return #VALUE!

xlAutoRegister xlAutoRegister12: Called when REGISTER XLM or xlfRegister C API is called without the function's return and argument types. It searches
the XLL internally to register the function with this information supplied.

xlAutoFree xlAutoFree12: Called when Excel is returned an XLOPER flagged as pointing to memory that the XLL needs to release.

The last three functions accept or return XLOPERs. In Excel 2007, they are supported by both XLOPER and XLOPER12.

The only required function is xlAutoOpen, without which the XLL does not load. When you are allocating memory or other resources within your DLL, you should also
implement xlFree xlFree12 to avoid memory leaks. You should implement xlAutoClose to clean up when the XLL is unloaded. The others can all be omitted.

The C API is socalled for a reason: Excel exchanges data using some standard C data types; the library functions are C name decorated; and the data structures are ANSI
C. With the appropriate experience, using C++ brings more manageability, readability, and stability to an XLL project. Therefore, the remaining portion of the article
assumes a basic understanding of C++ classes.

The data structures used by Excel to exchange data with XLLs are summarized in Table 1, which also provides the type letter used in the third argument to xlfRegister
when registering worksheet UDFs.

Table 1. Excel Data Structures used to exchange data with XLLs

Data Type Pass by Value Pass by Ref Pointer Comments

Boolean A L short 0=false or 1=true

Double B E

char * C, F Nullterminated ASCII byte string

unsigned char * D, G Counted ASCII byte string

[v12+] unsigned short * C%, F% Nullterminated Unicode widechar string

[v12+] unsigned short * D%, G% Counted Unicode wide character string

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 4/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

unsigned short [int] H DWORD, size_t, wchar_t

This documentation
[signed] short [int] is Iarchived and is not
M being maintained. 16bit

[signed long] int J N 32bit

FP K Floatingpoint array structure

[v12+] FP12 K% Larger grid floatingpoint array structure

XLOPER P Variabletype worksheet values and arrays

R Values, arrays and range references

[v12+] XLOPER12 Q Variabletype worksheet values and arrays

U Values, arrays and range references

The types C%, F%, D%, G%, K%, Q, and U are all new in Excel 2007 and are not supported in earlier versions. The string types F, F%, G, and G% are used for arguments
that are modifiedinplace. When XLOPER or XLOPER12 UDF function arguments are registered as types P or Q respectively, Excel converts singlecell references to
simple values and multicell references to arrays when preparing these arguments. P and Q types always arrive in your function as one of the following types:
xltypeNum, xltypeStr, xltypeBool, xltypeErr, xltypeMulti, xltypeMissing, or xltypeNil, but not xltypeRef or xltypeSRef because these are always dereferenced.

Argument 3 to xlfRegister, type_text, is a string of the previous codes. This string can also be suffixed by a number sign #that indicates that the function is a macro
sheet equivalent. The string can also be suffixed by an exclamation point ! indicating that the function is a macrosheet equivalent and/or that it is to be treated as
volatile. Declaring functions as macrosheet equivalents enables them to get the value of unrecalculated cells including the current value of the calling cell or cells and
to call XLM information functions. Functions registered as # and as taking R or U type arguments are volatile by default.

Excel 2007 also allows you to append a dollar sign symbol $ to indicate that the function is threadsafe. However, macrosheet functions are not considered thread
safe. Therefore, you cannot append both # and $ signs to a function's type type_text string. If an XLL attempts to register a function with both # and $, it fails.

Changes to XLLs in Excel 2007


Excel 2007 loads and runs any addin created for earlier versions. This does not mean that every XLL runs as intended in Excel 2007. Before you consider an XLL to be
fully compatible with Excel 2007, there are some hazards to be avoided. This section reviews some of the implicit or explicit assumptions that may no longer be true.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 5/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

One of the two major changes that the new structures address is the introduction of larger grids, for which rows and columns are counted with two new data typedefs:

C#This documentation is archived and is not being maintained.

typedefINT32RW;/*XL12Row*/
typedefINT32COL;/*XL12Column*/

Used in the new XLOPER12 and FP12 structures, these signed 32bit integers replace the WORD rows and BYTE columns used in XLOPER ranges and the WORD rows
used in XLOPER arrays and the FP data structure. The other major change is that Unicode strings are now supported in XLLs. The XLOPER12 is just an XLOPER that
includes the RW and COL types, and where the ASCII byte string is replaced by a Unicode string.

New Worksheet Functions


Analysis Toolpak ATP functions are now part of Excel 2007. Previously an XLL called an ATP addin function using xlUDF. In Excel 2007, you should replace such a call
with a call to xlfPrice, for example. There are also many new worksheet functions that you can call only when running Excel 2007. The C API returns xlretInvXlfn if these
are called in earlier versions. For more information, see Writing CrossVersion XLLs.

Strings
For the first time, Excel 2007 gives XLLs direct access to wide character Unicode strings of up to 32,767 2151 characters long. These strings have been supported in
worksheets for several versions now. This is a huge improvement on the previous C API where strings could not exceed 255 ASCII bytes. Byte strings are still supported,
still with the same length limits, through the C, D, F, and G argument types and the XLOPER xltypeStr.

In Microsoft Windows, conversion between byte strings and Unicode strings is localedependent. This means that the 255byte characters are converted up to and
down from wide Unicode characters in a way that depends on the locale settings of the system. The Unicode standard assigns unique characters to each code, but this
is not true of extended ASCII codes. You should remember this localespecific conversion. For example, it is possible for two unequal Unicode strings to compare equal
after conversion to byte strings.

In Excel 2007, any string that the user sees is typically represented internally in Unicode. Therefore, the most efficient way to exchange strings in Excel 2007 is to use
these. Earlier versions only allow you to access byte strings when interacting through the C API, although you can work with wide Unicode strings through string
Variants. These can be passed to a DLL or XLL from VBA or by using the Excel 2007 COM interface. When running Excel 2007, you should try to work with Unicode
strings whenever possible.

String Types Available to the Excel C API


Table 2 shows the C API xltypeStr XLOPERs.

Table 2. C API xltypeStr XLOPERs

Byte Strings: XLOPER Wide Character Strings: XLOPER12

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 6/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

All versions of Excel Excel 2007+ only

This
Max documentation
length: 255 extendedis ASCII
archived
bytesandMaximum
is not being maintained.
length 32,767 Unicode chars

First unsigned byte = length First wide character = length

Important

Do not assume nulltermination.

Table 3 shows the C/C++ strings.

Table 3. C/C++ strings

Byte Strings Wide Character Strings

Nullterminated char * "C" Nullterminated wchar_t * "C%" Maximum length 32,767 Unicode chars
Max length: 255 extended ASCII bytes

Lengthcounted unsigned char * "D" Lengthcounted wchar_t * "D%"

Converting One String Type to Another


The addition of new string types to XLLs creates the possibility that you might need to convert bytestrings to widechar or widechar strings to bytestrings.

When copying strings, ensure that the source string is not too long for the destination string buffer. It is a matter of implementation whether to fail or truncate in this
case. Table 4 shows the conversion and copying library routines.

Table 4. Conversion and copying library routines

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 7/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

This documentation is archived and is not being maintained.

Note that all the library functions shown in table 4 take a maximum string length argument. You should always supply this to avoid overrunning the Excellimited
buffers.

Consider the following:

When working with lengthcounted byte strings declared as [signed] char *, cast the length to BYTE to avoid negative results for strings longer than 127.

When copying a maximumlength nullterminated string to a lengthcounted string buffer, do not copy the null termination character because this could over
run the buffer.

When allocating new memory for nullterminated strings, allocate space for the null termination.

Set the null termination explicitly when copying lengthcounted strings to nullterminated strings, unless you are using an API function that always does this for
you, such as lstrcpynA.

When speed is important, and you know the number of bytes you are copying, use memcpy instead of strcpy, strncpy, wcscpy, or wcsncpy.

The following functions safely convert a lengthcounted byte string to a nullterminated C byte string, and a nullterminated C byte string to a lengthcounted byte
string. The first assumes a big enough target buffer is passed, and the second at most a 256byte buffer including the length byte:

C#

char*BcountToC(char*cStr,constchar*bcntStr)
{
BYTEcs_len=(BYTE)bcntStr[0];
if(cs_len)memcpy(cStr,bcntStr+1,cs_len);
cStr[cs_len]='\0';
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 8/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

returncStr;
}
#defineMAX_XL4_STR_LEN255u
This documentation is archived and is not being maintained.
char*CToBcount(char*bcntStr,constchar*cStr)
{
size_tcs_len=strlen(cStr);
if(cs_len>MAX_XL4_STR_LEN)cs_len=MAX_XL4_STR_LEN;
memcpy(bcntStr+1,cStr,cs_len);
bcntStr[0]=(BYTE)cs_len;
returnbcntStr;
}

Implications of Larger Grids in Code


In Microsoft Office Excel 2003, the maximum size of a singleblock range is 224 cells, which is well within the limits of a 32bit integer. In Excel 2007, the limit is 234 cells.
The twogigabyte memory limit that all applications are limited to is reached with a simple array of doubles at about 228 cells, so that recording the size of an
xltypeMulti array or an FP or FP12 with a signed 32bit integer is safe. However, to get the size of a huge range safely, such as the entire Excel 2007 grid, you need a 64
integer type such as __int64 or INT64.

Range Names
The rules determining what is and is not a valid range name change with Excel 2007. The maximum column is now XFD. For example, the formula =RNG1 is now
interpreted as a reference to the first cell in the 371st column unless the workbook is running in Compatibility mode, that is, when an Excel 2003 format workbook is
opened. If a user saves a 2003 workbook in the Excel 2007 format, Excel redefines names such as RNG1 as _RNG1 and alerts the user to that change. All workbook
occurrences are replaced except those in VBA code, breaking that code and possibly external references in other workbooks. You should check any code that creates,
validates, or looks for such names. One way to modify code so that it functions in both saved formats is to look for _RNG1 if RNG1 is not defined.

Handling Large Arrays and Out-of-Memory Conditions


In Excel 2007, XLOPER arrays xltypeMulti that are coerced XLOPER range types are limited in size by the Excel 32bit address space rather than by the number of rows
and columns or the width of the integers used to count them. With its much larger grids, Excel 2007 can hit this memory limit more easily. Attempts to coerce a range
reference explicitly within code using xlCoerce, or implicitly by registering exported function XLOPER arguments as type P, or XLOPER12s as type Q, fail if the range is
too large for the available memory. In the first case, a call to Excel4 or Excel12 fails with xlretFailed. In the latter case, the Excel returns #VALUE! to the calling cell.

For performance reasons, where there is a significant risk that a user may pass a huge array to your addin, you should either detect the size of the array and reject it if
over a certain limit, or, you should enable userbreaks of worksheet functions using xlAbort.

Handling Huge Range References and Recalculation


In Excel 2003, a reference could point to, at most, all 224 cells on a worksheet. Even if you were processing just a fraction of these, you could effectively hang the
computer from the user's point of view. You should, therefore, check range size to gauge whether to process in smaller chunks and, as with the processing of large
arrays, detect userbreaks using xlAbort. With Excel 2007, ranges can be up to roughly 1,000 times larger, so careful checking is even more important. A command that
might take one second to process the entire worksheet in Excel 2003 might, all other things being equal, take over 15 minutes in Excel 2007.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 9/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

More Function Arguments


Excel 2007 allows XLLs to export functions with up to 255 arguments. In practice, a function with that number of arguments, each with a distinct meaning, is probably
This documentation is archived and is not being maintained.
overcomplex and should be broken down. This number of arguments is much more likely to be used by a function that processes a variable number of similar types of
input.

Multithreaded Recalculation
You can configure Excel 2007 to use multiple threads to recalculate worksheet functions that are registered as threadsafe. This can lead to a reduction in calculation
times on multiprocessor computers, but can also be useful on singleprocessor computers, especially where UDFs are used to access functionality on a multithreaded
server. One advantage of XLLs over VBA, COM, and C# addins is that they can register their functions as threadsafe.

Access to New Commands and Worksheet Functions


The enumerated function definitions are extended to include all the worksheet functions that are added since Excel 97 version 9 and several new commands. The new
functions are shown in Table 5.

Table 5. New functions

xlfAccrint xlfCumprinc xlfImlog10 xlfQuotient

xlfAccrintm xlfDec2bin xlfImlog2 xlfRandbetween

xlfAmordegrc xlfDec2hex xlfImpower xlfReceived

xlfAmorlinc xlfDec2oct xlfImproduct xlfRoundbahtdown

xlfAveragea xlfDelta xlfImreal xlfRoundbahtup

xlfAverageif xlfDisc xlfImsin xlfRtd

xlfAverageifs xlfDollarde xlfImsqrt xlfSeriessum

xlfBahttext xlfDollarfr xlfImsub xlfSqrtpi

xlfBesseli xlfDuration xlfImsum xlfStdeva

xlfBesselj xlfEdate xlfIntrate xlfStdevpa

xlfBesselk xlfEffect xlfIseven xlfSumifs


https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 10/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xlfBessely xlfEomonth xlfIsodd xlfTbilleq


This documentation is archived and is not being maintained.
xlfBin2dec xlfErf xlfIsthaidigit xlfTbillprice

xlfBin2hex xlfErfc xlfLcm xlfTbillyield

xlfBin2oct xlfFactdouble xlfMaxa xlfThaidayofweek

xlfComplex xlfFvschedule xlfMduration xlfThaidigit

xlfConvert xlfGcd xlfMina xlfThaimonthofyear

xlfCountifs xlfGestep xlfMround xlfThainumsound

xlfCoupdaybs xlfGetpivotdata xlfMultinomial xlfThainumstring

xlfCoupdays xlfHex2bin xlfNetworkdays xlfThaistringlength

xlfCoupdaysnc xlfHex2dec xlfNominal xlfThaiyear

xlfCoupncd xlfHex2oct xlfOct2bin xlfVara

xlfCoupnum xlfHyperlink xlfOct2dec xlfVarpa

xlfCouppcd xlfIferror xlfOct2hex xlfViewGet

xlfCubekpimember xlfImabs xlfOddfprice xlfWeeknum

xlfCubemember xlfImaginary xlfOddfyield xlfWorkday

xlfCubememberproperty xlfImargument xlfOddlprice xlfXirr

xlfCuberankedmember xlfImconjugate xlfOddlyield xlfXnpv

xlfCubeset xlfImcos xlfPhonetic xlfYearfrac

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 11/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xlfCubesetcount xlfImdiv xlfPrice xlfYield

This documentation is archived


xlfCubevalue and is not being
xlfImexp maintained. xlfYielddisc
xlfPricedisc

xlfCumipmt xlfImln xlfPricemat xlfYieldmat

The new commands are shown in Table 6.

Table 6. New commands

xlcActivateNotes xlcInsertdatatable xlcOptionsSettings xlcUnprotectRevisions

xlcAddPrintArea xlcInsertMapObject xlcOptionsSpell xlcVbaactivate

xlcAutocorrect xlcLayout xlcPicklist xlcViewDefine

xlcClearPrintArea xlcMoveBrk xlcPivotTableChart xlcViewDelete

xlcDeleteNote xlcNewwebquery xlcPostDocument xlcViewShow

xlcEditodc xlcNormal xlcProtectRevisions xlcWebPublish

xlcHideallInkannots xlcOptionsMe xlcRmPrintArea xlcWorkgroupOptions

xlcHideallNotes xlcOptionsMenono xlcSheetBackground

xlcHidecurrNote xlcOptionsSave xlcTraverseNotes

The commands shown in Table 7 have been removed.

Table 7. Removed commands

xlcStart xlcVbaObjectBrowser

xlcVbaAddWatch xlcVbaReferences

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 12/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xlcVbaClearBreakpoints xlcVbaReset

This documentation isxlcVbaStepInto


xlcVbaDebugWindow archived and is not being maintained.

xlcVbaEditWatch xlcVbaStepOver

xlcVbaEnd xlcVbaToggleBreakpoint

xlcVbaInstantWatch

C API Functions: Excel4, Excel4v, Excel12, Excel12v


Experienced XLL developers should be very familiar with the old C API functions:

C#

int_cdeclExcel4(intxlfn,LPXLOPERoperRes,intcount,...);
/*followedbycountLPXLOPERs*/
intpascalExcel4v(intxlfn,LPXLOPERoperRes,intcount,LPXLOPERopers[]);

In Excel 2007, the new SDK also includes a source code module that contains definitions of two more C API functions that work identically but take XLOPER12
arguments. If these functions are called in an earlier version of Excel they both return xlretFailed:

C#

int_cdeclExcel12(intxlfn,LPXLOPER12operRes,intcount,...);
/*followedbycountLPXLOPER12s*/
intpascalExcel12v(intxlfn,LPXLOPER12operRes,intcount,LPXLOPER12opers[]);

Both functions return the same success and error values as Excel4 and Excel4v in earlier versions, but in Excel 2007 all four can return a new error: xlretNotThreadSafe,
defined as 128. This is returned whenever a function registered as threadsafe attempts to call a function that is not threadsafe, typically a macrosheet function or an
unsafe UDF.

The Add-in Manager Interface Functions


A couple of the xlAuto functions take or return XLOPERs. These still function as expected in Excel 2007, but XLOPER12 versions are now also recognized. The functions
affected are as follows:

C#
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 13/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xloper*__stdcallxlAutoRegister(xloper*p_name);
xloper*__stdcallxlAddInManagerInfo(xloper*p_arg);
This documentation is archived and is not being maintained.

The new functions are as follows:

C#

xloper12*__stdcallxlAutoRegister12(xloper12*p_name);
xloper12*__stdcallxlAddInManagerInfo12(xloper12*p_arg);

None of these four functions is required, and Excel uses default behavior if they are not present. In Excel 2007, if the XLOPER12 versions are present they are called in
preference to the XLOPER versions. When you are creating multiversion XLLs you should ensure that the two versions function equivalently.

Floating-Point Matrix Types


The following structure, registered as type K, has been supported in Excel for many versions. The structure is defined in the Excel 2007 xlcall.h SDK header file:

C#

typedefstruct
{
WORDrows;
WORDcolumns;
doublearray[1];//Startofarray[rows*columns]
}
FP;

Here is an example of the kind of assumption that could cause problems. Before Excel 2007, you could assume that the following code was safe, although admittedly
poor style:

C#

//Unsafefunction:returnstheoffset(from0)ofthecolumnwithmaxsum.
int__stdcallmax_column_index(FP*pArray)
{
intc,columns=pArray>columns;
intr,rows=pArray>rows;
double*p,column_sums[256];//Explicitassumption!!!

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 14/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

for(c=0;c<columns;c++)
column_sums[c]=0.0;//Overrunpossibleifcolumns>256!!!
This documentation is archived and is not being maintained.
for(r=0,p=pArray>array;r<rows;r++)
for(c=0;c<columns;c++)
column_sums[c]+=*p++;//overrunpossible!!!

intmax_index=0;
for(c=1;c<columns;c++)
if(column_sums[c]>column_sums[max_index])//overrun!!!
max_index=c;
returnmax_index;
}

Even though the FP type is not one of the new data types, in Excel 2007 this structure accommodates arrays that span the entire width of the new grid 214 columns,
but are truncated to 216 rows at most. In this case, the fix is very simple: dynamically allocate a buffer that is always the right size:

C#

double*column_sums=newdouble[columns];
//...
delete[]column_sums;
returnmax_index;

To enable more than 216 rows to be passed, Excel 2007 also supports a new data type, registered as K%:

C#

typedefstruct
{
RWrows;
COLScolumns;
doublearray[1];//Startofarray[rows*columns]
}
FP12;

XLCallVer
The signature for the XLCallVer function is as follows:

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 15/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

C#

This documentation is archived and is not being maintained.


intpascalXLCallVer(void);

In Microsoft Excel 97 through Excel 2003, XLCallVer returns 1280 = 0x0500 = 5*256, which indicates Excel version 5 the last time any change was made to the C API. In
Excel 2007, it returns 0x0C00, which similarly indicates version 12.

Although you can use this to determine whether the new C API is available at run time, you may prefer to detect the running version of Excel using
Excel4xlfGetWorkspace, &version, 1, &arg, where arg is a numeric XLOPER set to 2 and version is a string XLOPER, which can then be coerced to an integer. This is
because there are differences between Microsoft Excel 2000, Microsoft Excel 2002, and Excel 2003 that your addin may also need to detect. For example, changes were
made to the accuracy of some of the statistics functions and you may need to detect this.

C API Functions That Behave Differently in Different Versions


Generally, worksheet functions do not change the way they work from version to version, although numeric functions may improve accuracy. For the C APIonly
functions, however, the following three work differently in Excel 2007 than in earlier versions.

xlStack
This function now returns either the actual stack space or 64Kbytes, whichever is the lesser. The following code sample demonstrates how to get the stack space in any
version.

C#

double__stdcallget_stack(void)
{
if(gExcelVersion12plus)
{
xloper12retval;
if(xlretSuccess!=Excel12(xlStack,&retval,0))
return1.0;

if(retval.xltype==xltypeInt)
return(double)retval.val.w;//returnsmin(64Kb,actualspace)
//Thisisnotthereturnedtype,butwasreturnedin
//anExcel12beta,sothecodeislefthere.
if(retval.xltype==xltypeNum)
returnretval.val.num;
}
else
{
xloperretval;
if(xlretSuccess!=Excel4(xlStack,&retval,0))
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 16/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

return1.0;

if(retval.xltype==xltypeInt)
This documentation is archived and is not being maintained.
return(double)(unsignedshort)retval.val.w;
}
return1.0;
}

xlGetHwnd
In Excel 2003, xlGetHwnd returns an xltypeInt XLOPER containing a 2byte short, the low part of the full Windows HWND handle for Excel. The full handle must be
obtained using the Windows API EnumWindows as shown in this section. In Excel 2007, when called using Excel12, the returned xltypeInt XLOPER12 contains a 4byte
signed integer which is the full handle. Note that even when called in Excel 2007, Excel4 only returns the low part of the handle.

C#

HWNDget_xl_main_handle(void)
{
if(gExcelVersion12plus)//xlGetHwndreturnsfullhandle
{
xloper12main_xl_handle;
if(Excel12(xlGetHwnd,&main_xl_handle,0)!=xlretSuccess)
return0;
return(HWND)main_xl_handle.val.w;
}
else//xlGetHwndreturnslowhandleonly
{
xlopermain_xl_handle;
if(Excel4(xlGetHwnd,&main_xl_handle,0)!=xlretSuccess)
return0;
get_hwnd_enum_structeproc_param={main_xl_handle.val.w,0};
EnumWindows((WNDENUMPROC)get_hwnd_enum_proc,(LPARAM)&eproc_param);
returneproc_param.full_handle;
}
}

#defineCLASS_NAME_BUFFER_SIZE50

typedefstruct
{
shortmain_xl_handle;
HWNDfull_handle;
}
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 17/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

get_hwnd_struct;

//ThecallbackfunctioncalledbyWindowsforeverytoplevelwindow
This documentation is archived and is not being maintained.
BOOL__stdcallget_hwnd_enum_proc(HWNDhwnd,get_hwnd_struct*p_enum)
{
//CheckifthelowwordofthehandlematchesExcel's
if(LOWORD((DWORD)hwnd)!=p_enum>main_xl_handle)
returnTRUE;//keepiterating

charclass_name[CLASS_NAME_BUFFER_SIZE+1];
//Ensurethatclass_nameisalwaysnullterminated
class_name[CLASS_NAME_BUFFER_SIZE]=0;
GetClassName(hwnd,class_name,CLASS_NAME_BUFFER_SIZE);
//DoacaseinsensitivecomparisonfortheExcelmainwindowclassname
if(_stricmp(class_name,"xlmain")==0)
{
p_enum>full_handle=hwnd;
returnFALSE;//TellsWindowstostopiterating
}
returnTRUE;//TellsWindowstocontinueiterating
}

xlGetInst
As with xlGetHwnd, when called using Excel12, the returned xltypeInt XLOPER12 contains the full running instance handle, whereas, Excel4 returns an xltypeInt
XLOPER containing only the low part of the handle.

User Interface Customization


In earlier versions of Excel, you could use XLL code to customize menu bars, menus, command bars, or toolbars using commands such as xlcAddBar, xlcAddMenu,
xlcAddCommand, xlcShowBar, xlcAddToolbar, xlcAddTool, and xlcShowToolbar. These commands are still supported but, because the old menubar and
commandbar structures are replaced, they place access to your XLL commands in the Addin group of the Ribbon and therefore may not give your users the intended
interface.

You can only customize the UI in the 2007 Microsoft Office release using managed code. One approach to UI customization in Excel 2007 is to have a separate managed
code resource or addin, within which the functions that customize the UI reside. You can then tightly couple it to your XLL, calling back into your XLL code to invoke
the commands and functions it contains.

Writing Cross-Version XLLs


The following sections review how to write XLLs that are compatible across multiple versions of Excel.
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 18/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

Some Useful Constant Definitions


You This documentation
should is archived
consider including andsuch
definitions is not
as being
these inmaintained.
your XLL project code and replacing all instances of literal numbers used in this context. This will greatly
clarify code that is versionspecific, and reduce the probability of versionrelated bugs in the form of innocuouslooking numbers.

C#

#defineMAX_XL11_ROWS65536
#defineMAX_XL11_COLS256
#defineMAX_XL12_ROWS1048576
#defineMAX_XL12_COLS16384
#defineMAX_XL11_UDF_ARGS30
#defineMAX_XL12_UDF_ARGS255
#defineMAX_XL4_STR_LEN255u
#defineMAX_XL12_STR_LEN32767u

Getting the Running Version


You should detect which version is running using Excel4(xlfGetWorkspace,&version,1,&arg), where arg is a numeric XLOPER set to 2 and version is a string
XLOPER which can then be coerced to an integer. For Excel 2007, this is 12. You should do this in, or from, xlAutoOpen. You can also call XLCallVer, but this does not
indicate which of the pre2007 versions you are running.

Linking to the xlcall32 Library and the C API Functions


Based on the Excel 97 SDK and Framework project, the standard way of linking to the xlcall32 library is to include a reference in the project to the xlcall32.lib import
library. Alternatively, you can link explicitly to xlcall32.dll at runtime using LoadLibrary and GetProcAddress. For projects built like this, the library is linked at compile
time and its exports are prototyped in the usual manner. For example:

C#

#ifdef__cplusplus
extern"C"{
#endif
int_cdeclExcel4(intxlfn,LPXLOPERoperRes,intcount,...);
//...
#ifdef__cplusplus
}//extern"C"blockend
#endif

At run time, when the XLL is loaded by Excel, it is implicitly linked to xlcall32.dll.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 19/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

Any addin that was built linked at compiletime with an earlier version of the import library runs with Excel 2007, but cannot access the Excel12 and Excel12v
callbacks because they are not defined. Code that uses the Excel 2007 SDK version of xlcall.h and C++ source file [name?].cpp and which is linked to the Excel 2007
version
Thisofdocumentation
xlcall32.lib, can safely invokeand
is archived these
is functions
not beinginmaintained.
all recent versions of Excel. If called from an earlier version than Excel 2007, they just return xlretFailed. This is
only a failsafe, so you should ensure that your code is aware of the running version and calls the appropriate callback.

Creating Add-ins That Export Dual Interfaces


Consider an XLL function that takes a string and returns a single argument that can be any of the worksheet data types or a range. In Excel 2003 and Excel 2007 you
could export a function registered as type RD and prototyped as follows, where the string is passed as a lengthcounted byte string:

C#

xloper*__stdcallmy_xll_fn(unsignedchar*arg);

First, this functions correctly in all recent versions of Excel but is subject to the limitations of the old C API strings. Second, although Excel 2007 can pass and accept
XLOPERs, internally it converts them to XLOPER12s, so there is an implicit conversion overhead in Excel 2007 that is not there when the code runs in Excel 2003. Third,
maybe this function can be made threadsafe, but if the type string is changed to RD$ registration fails in Excel 2003. For all these reasons, ideally, you should export a
function for your Excel 2007 users that was registered as UD%$ and prototyped as follows:

C#

xloper12*__stdcallmy_xll_fn_v12(wchar_t*arg);

Another reason why you may want to register a different function when running Excel 2007 is that it allows XLL functions to take up to 255 arguments the old limit is
30. Fortunately, you can have the benefits of both by exporting both versions from your project. Then you detect the running Excel version and conditionally register
the most appropriate function.

There are many ways that you can manage the data passed when registering the XLL's exports within a project.

One simple way is to define a data structure, called ws_func_export_data for example as in the following code, and then declare and initialize an array of
ws_func_export_data which can then be used by your XLL code to initialize the XLOPERs or XLOPER12s passed to xlfRegister. For example:

C#

#defineXL12_UDF_ARG_LIMIT255
typedefstruct
{
//REQUIRED(ifv12+stringsundefinedusev11strings):
char*name_in_code;//RegArg2:Functionnameasincode(v11)
char*types;//RegArg3:Returntypeandargumenttypes(v11)

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 20/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

char*name_in_code12;//RegArg2:Functionnameasincode(v12+)
char*types12;//RegArg3:Returntypeandargumenttypes(v12+)
char*ws_name;//RegArg4:Fnnameasitappearsonworksheet
This documentation is archived and is not being maintained.
char*arg_names;//RegArg5:Argumentnames(Excel11:max30)
char*arg_names12;//RegArg5:Argumentnames(Excel12+:max64)
//OPTIONAL:
char*fn_category;//RegArg7:FunctioncategoryforFunctionWizard
char*help_file;//RegArg9:Helpfile(optional)
char*fn_description;//RegArg10:Functiondescriptiontext(optional)
char*arg_help[MAX_XL12_UDF_ARGS11];//RegArg11...:Arghelptext
}
ws_func_export_data;

Note that whatever the registration function does with this data, only one worksheet function name is provided so that worksheets are not aware and do not need to
know which function is called. Here is an example of a function that calls standard library functions to reverse a worksheet string:

C#

//Excel11:Registerastype"1F"
void__stdcallreverse_text_xl4(char*text){strrev(text);}

//Excel12+:Registerastype"1F%$"(iflinkingwiththreadsafelibrary)
void__stdcallreverse_text_xl12(wchar_t*text){wcsrev(text);}

You could then initialize the structure for this function as follows:

C#

ws_func_export_dataWsFuncExports[1]=
{
{
"reverse_text_xl4",
"1F",
"reverse_text_xl12",
"1F%$",
"Reverse",
"Text",
"",//arg_names12
"Text",//functioncategory
"",//helpfile
"Reversetext",

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 21/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

"Text",
},
};
This documentation is archived and is not being maintained.

The previous strings are nullterminated byte strings. Any code that uses these to initialize XLOPERs must first convert them to lengthcounted strings. They would also
need to be converted from bytes to Unicode if used to initialize XLOPER12s. Alternatively, you could initialize these strings with a leading space, which other code can
overwrite with the strings' lengths. However, this can cause problems with some compilers running in debug mode. You could easily modify the previous structure
definition to pass Unicode strings to Excel when running Excel 2007. This would also require that you modify the code that used the structure.

This approach leads to the possibility that a worksheet running in Excel 2003 could display different results than the same sheet running in Excel 2007. For example,
Excel 2003 maps a Unicode string in an Excel 2003 worksheet cell to an ASCII bytestring and truncates it before passing it to an XLL function call. Excel 2007 will pass an
unconverted Unicode strong to an XLL function registered in the correct way. This could lead to a different return result. You should be aware of this possibility and the
consequences to your users, not just in the upgrade to Excel 2007. For example, some builtin numeric functions were improved between Excel 2000 and Excel 2003.

Wrapping the Data Types in a Common Container Class or Structure


In general, an XLL worksheet function performs the following actions:

Checks the validity of the inputs.

Interprets the inputs in the context of one another.

Returns specific errors if the inputs are not what they should be.

Populates data structures.

Calls some deeper core code.

Processes the return value from core code and returns something appropriate to Excel.

Where you are providing two exported interfaces, as outlined here, having to duplicate all this logic is not ideal, but if the argument data types are all different, what
choice do you have? The answer is to wrap Excel data types in a common container. There are many possible approaches, but let us for the moment constrain the
discussion of containing XLOPERs and XLOPER12s. The solution outlined here is to create a C++ class that understands XLOPERs and XLOPER12s and in this example
contains an instance of both. The following examples discuss a C++ class cpp_xloper, which is described fully in the second edition of the author's book listed at the
end of the article.

The class should ideally have a constructor that makes a shallow copy, by default, of a supplied XLOPER or XLOPER12 initializer. Copies are shallow to speed up
interpretation of readonly UDF arguments. It should also provide accessor functions to enable the extraction and modification of type and value. The exported
functions then only need to convert their XLOPER or XLOPER12 arguments to this common type, call a common function that performs the real task, and process that
function's return values. Here is an example using the class cpp_xloper:

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 22/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

C#

This documentation is archived and is not being maintained.


xloper*__stdcallmy_function_xl4(xloper*arg)
{
cpp_xloperArg(arg);//constructormakesshallowcopy
cpp_xloperRetVal;//passedbyreftoretrievereturnvalue
my_core_function(RetVal,Arg);
returnRetVal.ExtractXloper();
}

xloper12*__stdcallmy_function_xl12(xloper12*arg)
{
cpp_xloperArg(arg);//constructormakesshallowcopy
cpp_xloperRetVal;//passedbyreftoretrievereturnvalue
my_core_function(RetVal,Arg);
returnRetVal.ExtractXloper12();
}

voidmy_core_function(cpp_xloper&RetVal,cpp_xloper&Arg)
{
doubled;
if(Arg.IsMissing()||Arg.IsNil())
RetVal.SetToError(xlerrValue);
elseif(Arg.IsNum()&&(d=(double)Arg)>=0.0)
RetVal=sqrt(d);//overloadedassignmentoperator
else
RetVal.SetToError(xlerrNum);
}

It is assumed the methods ExtractXloper and ExtractXloper12 return pointers to a threadsafe static XLOPER and XLOPER12 respectively, with the appropriate
memoryfree bits set where necessary.

To minimize the overhead of all this wrapping, not only should the constructor make shallow copies but, internally, it should recognize the running version and convert
XLOPERs to XLOPER12s and call Excel12 instead of Excel4 when running Excel 2007. This is because in Excel 2007, if Excel4 is called, the XLOPER arguments are
converted up to XLOPER12s and the return value converted back down to an XLOPER. Getting the class to use the appropriate type and callback avoids this conversion
on every call.

Wrapping the C API Functions


Following on from the previous section, if my_core_function needs to call back into Excel through the C API, it must convert the cpp_xloperback into an XLOPER or an
XLOPER12 and then call either Excel4 or Excel12 depending on the running version. One solution to this is to wrap the functions Excel4, Excel4v, Excel12, and
Excel12v into cpp_xloper as class member functions. Then you could rewrite my_core_function as:
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 23/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

C#

This documentation is archived and is not being maintained.


voidmy_core_function(cpp_xloper&RetVal,cpp_xloper&Arg)
{
if(!Arg.IsNum()||Arg.Excel(xlfSqrt,1,&Arg)!=xlretSuccess)
RetVal.SetToError(xlerrValue);
}

where cpp_xloper::Excel places the return value directly into Arg. To do this and still provide flexibility so that you can call this function with XLOPER, XLOPER12, or
cpp_xloper arguments, you should create a number of overloaded member functions:

C#

intExcel(intxlfn);//notstrictlynecessary,butsimplifiestheothers
intExcel(intxlfn,intcount,constxloper*p_op1,...);
intExcel(intxlfn,intcount,constxloper12*p_op1,...);
intExcel(intxlfn,intcount,constcpp_xloper*p_op1,...);
intExcel(intxlfn,intcount,constxloper*p_array[]);
intExcel(intxlfn,intcount,constxloper12*p_array[]);
intExcel(intxlfn,intcount,constcpp_xloper*p_array[]);

Note that it is assumed that the caller of the variable argument versions of these functions does not mix argument types. Note also that the use of const here requires
the addition of const to the definitions of Excel4v and Excel12v.

After implementing such a wrapper, you should not call the C API functions directly. Another advantage of using this approach is that you can contain the memory
management of the returned value within the class. If Excel returns a string, the class can set a flag telling it to call xlFree before overwriting or destroying that instance.
You can also build additional checks into these wrappers. For example, you can check that the count is not less than zero or greater than the versionspecific limit. In
this case, you may want to define and return an additional error:

C#

#definexlretNotCalled1//CAPInotcalled

Here is an example implementation of one of these functions, where variables prefixed with m_ are class member variables; the flags m_XLtoFree12 and m_XLtoFree
indicate to the class to call xlFree to release memory; and m_Op and m_Op12 are the class' internal copies of the XLOPER and XLOPER12 data structures respectively:

C#

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 24/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

intcpp_xloper::Excel(intxlfn,intcount,constcpp_xloper*p_op1,...)
{
This documentation is archived and is not being maintained.
if(xlfn<0||count<0||count>(gExcelVersion12plus?
MAX_XL12_UDF_ARGS:MAX_XL11_UDF_ARGS))
returnxlretNotCalled;

if(count==0||!p_op1)//defaultto0andNULLifomitted
returnExcel(xlfn);//Callasimplerversionofthisfunction

va_listarg_ptr;
va_start(arg_ptr,p_op1);//Initialize

if(gExcelVersion12plus)
{
constxloper12*xloper12_ptr_array[MAX_XL12_UDF_ARGS];
xloper12_ptr_array[0]=&(p_op1>m_Op12);
cpp_xloper*p_cpp_op;

for(inti=1;i<count;i++)//retrieveasptrstocpp_xlopers
{
p_cpp_op=va_arg(arg_ptr,cpp_xloper*);
xloper12_ptr_array[i]=&(p_cpp_op>m_Op12);
}
va_end(arg_ptr);//Reset

xloper12temp;
m_ExcelRtnCode=Excel12v(xlfn,&temp,count,xloper12_ptr_array);
Free();

if(m_ExcelRtnCode==xlretSuccess)
{
m_Op12=temp;//shallowcopy
m_XLtoFree12=true;
}
}
else//gExcelVersion<12
{
constxloper*xloper_ptr_array[MAX_XL11_UDF_ARGS];
xloper_ptr_array[0]=&(p_op1>m_Op);
cpp_xloper*p_cpp_op;

for(inti=1;i<count;i++)//retrieveasptrstocpp_xlopers

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 25/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

{
p_cpp_op=va_arg(arg_ptr,cpp_xloper*);
xloper_ptr_array[i]=&(p_cpp_op>m_Op);
This documentation is archived and is not being maintained.
}
va_end(arg_ptr);//Reset

xlopertemp;
m_ExcelRtnCode=Excel4v(xlfn,&temp,count,xloper_ptr_array);
Free();

if(m_ExcelRtnCode==xlretSuccess)
{
m_Op=temp;//shallowcopy
m_XLtoFree=true;
}
}
returnm_ExcelRtnCode;
}

New Worksheet Functions and Analysis Toolpak Functions


Unlike earlier versions of Excel, Analysis Toolpak ATP functions have been incorporated into Excel 2007. Previously, an XLL could call an ATP function in the following
way using xlUDF:

C#

doublecall_ATP_example(void)
{
xlopersettlement,maturity,coupon,yield,
redepmtion_value,num_coupons,rate_basis,price;

//InitialisethedatatypestobepassedtotheATPfunctionPRICE
settlement.xltype=maturity.xltype=coupon.xltype=
yield.xltype=redepmtion_value.xltype=
num_coupons.xltype=rate_basis.xltype=xltypeNum;

//SetthevaluestobepassedtotheATPfunctionPRICE
settlement.val.num=39084.0;//2Jan2007
maturity.val.num=46706.0;//15Nov2027
coupon.val.num=0.04;
yield.val.num=0.05;
redepmtion_value.val.num=1.0;//100%offacevalue

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 26/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

num_coupons.val.num=1.0;//Annualcoupons
rate_basis.val.num=1.0;//Act/Act
This documentation is archived and is not being maintained.
xloperfn;
fn.xltype=xltypeStr;
fn.val.str="\005""PRICE";
if(Excel4(xlUDF,&price,8,&fn,&settlement,&maturity,
&coupon,&yield,&redepmtion_value,&num_coupons,
&rate_basis)!=xlretSuccess||price.xltype!=xltypeNum)
return1.0;//anerrorvalue
returnprice.val.num;
}

In Excel 2007, you should replace the call into Excel with something like this, where gExcelVersion is an integer variable that has global scope within your project, and
is initialized during the call to xlAutoOpen.

C#

intxl_ret_val;
if(gExcelVersion12plus)
{
xl_ret_val=Excel4(xlfPrice,&price,7,&settlement,
&maturity,&coupon,&yield,&redepmtion_value,
&num_coupons,&rate_basis);
}
else//gExcelVersion<12
{
xloperfn;
fn.xltype=xltypeStr;
fn.val.str="\005""PRICE";
xl_ret_val=Excel4(xlUDF,&price,8,&fn,&settlement,
&maturity,&coupon,&yield,&redepmtion_value,
&num_coupons,&rate_basis);
}
if(xl_ret_val!=xlretSuccess||price.xltype!=xltypeNum)
return1.0;//anerrorvalue
returnprice.val.num;

You can make the function more versionindependent and efficient in both Excel 2003 and Excel 2007 with the use of a container such as cpp_xloper. For example:

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 27/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

C#

This documentation is archived and is not being maintained.


doublecall_ATP_example_3(void)
{
cpp_xloperSettlement(39084.0);//2Jan2007
cpp_xloperMaturity(46706.0);//15Nov2027
cpp_xloperPrice,Coupon(0.04),YTM(0.05);
cpp_xloperRedepmtionValue(1.0);//100%offace
cpp_xloperNumCoupons(1.0);//Annualcoupons
cpp_xloperRateBasis(1.0);//Act/Act
intxl_ret_val;

if(gExcelVersion12plus)
{
xl_ret_val=Price.Excel(xlfPrice,7,&Settlement,
&Maturity,&Coupon,&YTM,&RedepmtionValue,
&NumCoupons,&RateBasis);
}
else
{
cpp_xloperFn("PRICE");
xl_ret_val=Price.Excel(xlUDF,8,&Fn,&Settlement,
&Maturity,&Coupon,&YTM,&RedepmtionValue,
&NumCoupons,&RateBasis);
}
if(xl_ret_val!=xlretSuccess||!Price.IsNum())
return1.0;//anerrorvalue
return(double)Price;
}

If you try to call new C API worksheet functions in earlier versions, you get anxlretInvXlfn error.

Writing Thread-Safe XLLs and Worksheet Functions


Earlier versions of Excel used a single thread for all worksheet calculations. On a multiprocessor computer, or on a singleprocessor computer that the user has explicitly
configured to use multiple threads, Excel 2007 tries to balance the load between the main thread and one or more additional threads, which the operating system
allocates across all the processors. On a dualprocessor or dualcore computer, this can speed up the recalculation time by at most a factor of 2, contingent on the
topology of the dependency tree within the workbooks and how many of the functions involved are threadsafe.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 28/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

Excel 2007 uses one thread its main thread to call all commands, threadunsafe functions, xlAuto functions except xlAutoFree, and COM and VBA functions.

XLL developers can create is


This documentation threadsafe functions
archived and is notprovided that they observe a few simple rules:
being maintained.

Do not call resources in other DLLs that may not be threadsafe.

Do not make any threadunsafe calls through the C API or COM.

Protect resources that could be used simultaneously by more than one thread using critical sections.

Use threadlocal memory for threadspecific storage, and replace static variables within functions with threadlocal variables.

When Excel is returned an XLOPER or XLOPER12 with xlbitDllFree set, it calls xlAutoFree on the same thread before calling any other functions on that thread. This is
true of all functions, threadsafe or not, and it avoids the risk of a threadlocal XLOPER getting reused before memory associated with it can be freed.

Calling C API Functions from Thread-Safe Functions


VBA and COM addin functions are not considered threadsafe. In addition to C API commands such as xlcDefineName, which no worksheet function is allowed to call,
threadsafe functions cannot access XLM information functions. Neither can you register threadsafe XLL functions as macrosheet equivalents by appending a number
sign # to the type string. The consequences are that a threadsafe function cannot:

Read the value of an uncalculated cell including the calling cell.

Call functions such as xlfGetCell, xlfGetWindow, xlfGetWorkbook, and xlfGetWorkspace and other information functions.

Define or delete XLLinternal names using xlfSetName.

The one XLM exception is xlfCaller which is threadsafe. If the caller was a worksheet cell or range, xlfCaller returns a reference. However, you cannot safely coerce this
resulting reference to a value using xlCoerce in a threadsafe function because this would return xlretUncalced. Registering the function with # addresses this problem,
but in this case, the function is a macrosheet equivalent, and therefore, is not considered threadsafe. This prevents functions that return the previous value, such as
when a certain error condition exists, from being threadsafe.

It should be noted that the C APIonly functions are all threadsafe:

xlCoerce although coercion of uncalculatedcell references fails

xlFree

xlStack

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 29/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xlSheetId

xlSheetNm
This documentation is archived and is not being maintained.
xlAbort except that it cannot be used to clear a break condition

xlGetInst

xlGetHwnd

xlGetBinaryName

xlDefineBinaryName

The one exception is xlSet which is a commandequivalent and so cannot be called from any worksheet function.

All the Excel 2007 builtin worksheet functions and their C API equivalents are threadsafe except for the following:

PHONETIC

CELL when either of the "format" or "address" arguments are used

INDIRECT

GETPIVOTDATA

CUBEMEMBER

CUBEVALUE

CUBEMEMBERPROPERTY

CUBESET

CUBERANKEDMEMBER

CUBEKPIMEMBER

CUBESETCOUNT

ADDRESS where the fifth parameter the sheet_name is given

Any database function such as DSUM or DAVERAGE that refers to an Excel PivotTable
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 30/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

ERROR.TYPE

This documentation is archived and is not being maintained.


HYPERLINK

Resources Used by Multiple Threads


You should protect read/write memory that can be accessed by more than one thread with the use of critical sections. You need a named critical section for each block
of memory you want to protect. You can initialize these during the call to xlAutoOpen, and release them and set them to null during the call to xlAutoClose. You
should contain each access to the protected block with calls to EnterCriticalSection and LeaveCriticalSection. Only one thread is permitted to be in the critical section
at any time. Here is an example of the initialization, uninitialization, and use of a section called g_csSharedTable:

C#

CRITICAL_SECTIONg_csSharedTable;//globalscope(ifrequired)
boolxll_initialised=false;//modulescope

int__stdcallxlAutoOpen(void)
{
if(xll_initialised)
return1;
//Otherinitialisationomitted
InitializeCriticalSection(&g_csSharedTable);
xll_initialised=true;
return1;
}

int__stdcallxlAutoClose(void)
{
if(!xll_initialised)
return1;
//Othercleaningupomitted
DeleteCriticalSection(&g_csSharedTable);
xll_initialised=false;
return1;
}

boolread_shared_table_element(unsignedintindex,double&d)
{
if(index>=SHARED_TABLE_SIZE)returnfalse;
EnterCriticalSection(&g_csSharedTable);
d=shared_table[index];

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 31/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

LeaveCriticalSection(&g_csSharedTable);
returntrue;
}
This documentation is archived and is not being maintained.
boolset_shared_table_element(unsignedintindex,doubled)
{
if(index>=SHARED_TABLE_SIZE)returnfalse;
EnterCriticalSection(&g_csSharedTable);
shared_table[index]=d;
LeaveCriticalSection(&g_csSharedTable);
returntrue;
}

Another, perhaps safer, way of protecting a block of memory is to create a class that contains its own CRITICAL_SECTION and whose constructor, destructor, and
accessor methods take care of its use. This approach has the added advantage of protecting objects that may be initialized before xlAutoOpen is run, or survive after
xlAutoClose is called, but you should be careful about creating too many critical sections and slowing down the operating system unnecessarily.

Where you have code that needs access to more than one block of protected memory at the same time, you need to be very careful about the order in which the critical
sections are entered and exited. For example, the following two functions could create a deadlock:

C#

boolcopy_shared_table_element_A_to_B(unsignedintindex)
{
if(index>=SHARED_TABLE_SIZE)returnfalse;
EnterCriticalSection(&g_csSharedTableA);
EnterCriticalSection(&g_csSharedTableB);
shared_table_B[index]=shared_table_A[index];
LeaveCriticalSection(&g_csSharedTableA);
LeaveCriticalSection(&g_csSharedTableB);
returntrue;
}
boolcopy_shared_table_element_B_to_A(unsignedintindex)
{
if(index>=SHARED_TABLE_SIZE)returnfalse;
EnterCriticalSection(&g_csSharedTableB);
EnterCriticalSection(&g_csSharedTableA);
shared_table_A[index]=shared_table_B[index];
LeaveCriticalSection(&g_csSharedTableA);
LeaveCriticalSection(&g_csSharedTableB);
returntrue;
}

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 32/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

If the first function on one thread enters g_csSharedTableAwhile the second on another thread enters g_csSharedTableB, both threads hang. The correct approach
is to enter in a consistent order and exit in the reverse order, as follows:
This documentation is archived and is not being maintained.
C#

EnterCriticalSection(&g_csSharedTableA);
EnterCriticalSection(&g_csSharedTableB);
//codethataccessesbothblocks
LeaveCriticalSection(&g_csSharedTableB);
LeaveCriticalSection(&g_csSharedTableA);

Where possible, it is better from a threadcooperation point of view to isolate access to distinct blocks, as shown here:

C#

boolcopy_shared_table_element_A_to_B(unsignedintindex)
{
if(index>=SHARED_TABLE_SIZE)returnfalse;
EnterCriticalSection(&g_csSharedTableA);
doubled=shared_table_A[index];
LeaveCriticalSection(&g_csSharedTableA);
EnterCriticalSection(&g_csSharedTableB);
shared_table_B[index]=d;
LeaveCriticalSection(&g_csSharedTableB);
returntrue;
}

Where there is a lot of contention for a shared resource, such as frequent shortduration access requests, you should consider using the critical section's ability to spin.
This is a technique that makes waiting for the resource less processorintensive. To do this, you can use either use InitializeCriticalSectionAndSpinCountwhen
initializing the section or SetCriticalSectionSpinCount after the critical section has been initialized, to set the number of times the thread loops before waiting for
resources to become available. The wait operation is expensive, so spinning avoids this if the resource is freed in the meantime. On a single processor system, the spin
count is effectively ignored, but you can still specify it without doing any harm. The memory heap manager uses a spin count of 4000. For more information about the
use of critical sections, you should refer to the Critical Sections Object topic in the Platform SDK documentation.

Declaring and Using Thread-Local Memory


For example, consider a function that returns a pointer to an XLOPER:

C#

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 33/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

xloper*__stdcallmtr_unsafe_example(xloper*arg)
{
This documentation is archived and is not being maintained.
staticxloperret_val;//memorysharedbyallthreads!!!
//codesetsret_valtoafunctionofarg...
return&ret_val;
}

This function is not threadsafe because it is possible for one thread to return the static XLOPER while another is overwriting it. The probability of this occurring is
greater still if the XLOPER needs to be passed to xlAutoFree. One solution is to allocate memory for a return XLOPER and implement xlAutoFree so that the XLOPER
memory itself is freed:

C#

xloper*__stdcallmtr_safe_example_1(xloper*arg)
{
xloper*p_ret_val=newxloper;//mustbefreedbyxlAutoFree
//codesetsret_valtoafunctionofarg...
p_ret_val.xltype|=xlbitDLLFree;//Alwaysneededregardlessoftype
returnp_ret_val;//xlAutoFreemustfreep_ret_val
}

This approach is simpler than the approach outlined in the next example, which relies on the TLS API, but it has a few disadvantages. For one, Excel has to call
xlAutoFree whatever the type of the returned XLOPER. And second, if the newlyallocated XLOPER is a string populated in a call to Excel4, there is no easy way to
inform xlAutoFree of the need to free the string using xlFree before using delete to free p_ret_val, requiring that the function make a DLLallocated copy.

A solution that avoids these limitations is to populate and return a threadlocal XLOPER, an approach that requires that xlAutoFree does not free the XLOPER pointer
itself.

C#

xloper*get_thread_local_xloper(void);

xloper*__stdcallmtr_safe_example_2(xloper*arg)
{
xloper*p_ret_val=get_thread_local_xloper();
//codesetsret_valtoafunctionofargsettingxlbitDLLFreeor
//xlbitXLFreeifrequired
returnp_ret_val;//xlAutoFreemustnotfreethispointer!
}

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 34/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

The next question is how to set up and retrieve the threadlocal memory. This is done using the Thread Local Storage TLS API. The first step is to obtain a TLS index
using TlsAlloc, which must ultimately be released using TlsFree. Both are best accomplished from DllMain:
This documentation is archived and is not being maintained.
C#

//Thisimplementationjustcallsafunctiontosetupthreadlocalstorage
BOOLTLS_Action(DWORDReason);

__declspec(dllexport)BOOL__stdcallDllMain(HINSTANCEhDll,DWORDReason,void*Reserved)
{
returnTLS_Action(Reason);
}
DWORDTlsIndex;//onlyneedsmodulescopeifallTLSaccessinthismodule

BOOLTLS_Action(DWORDDllMainCallReason)
{
switch(DllMainCallReason)
{
caseDLL_PROCESS_ATTACH://TheDLLisbeingloaded
if((TlsIndex=TlsAlloc())==TLS_OUT_OF_INDEXES)
returnFALSE;
break;

caseDLL_PROCESS_DETACH://TheDLLisbeingunloaded
TlsFree(TlsIndex);//ReleasetheTLSindex.
break;
}
returnTRUE;
}

After you obtain the index, the next step is to allocate a block of memory for each thread. DllMain in the DynamicLink Library Reference recommends doing this every
time DllMain is called with a DLL_THREAD_ATTACH event, and freeing the memory on every DLL_THREAD_DETACH, but following this advice would cause your DLL to
perform unnecessary allocation for threads that Excel does not use for recalculation. Instead, it is better to use an allocateonfirstuse strategy. First, you need to define
a structure that you want to allocate for each thread. For the previous example, the following suffices:

C#

structTLS_data
{
xloperxloper_shared_ret_val;

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 35/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

//Addotherrequiredthreadlocaldatahere...
};
This documentation is archived and is not being maintained.
The following function gets a pointer to the threadlocal instance, or allocates one if this is the first call:

C#

TLS_data*get_TLS_data(void)
{
//Getapointertothisthread'sstaticmemory
void*pTLS=TlsGetValue(TlsIndex);
if(!pTLS)//NoTLSmemoryforthisthreadyet
{
if((pTLS=calloc(1,sizeof(TLS_data)))==NULL)
//Displaysomeerrormessage(omitted)
returnNULL;
TlsSetValue(TlsIndex,pTLS);//Associatethisthisthread
}
return(TLS_data*)pTLS;
}

Now you can see how the threadlocal XLOPER memory is obtained. First, you get a pointer to the thread's instance of TLS_data, and then you return a pointer to the
XLOPER that it contains:

C#

xloper*get_thread_local_xloper(void)
{
TLS_data*pTLS=get_TLS_data();
if(pTLS)
return&(pTLS>xloper_shared_ret_val);
returnNULL;
}

By adding an XLOPER12 to TLS_data and a get_thread_local_xloper12 access function you can write XLOPER12 versions of mtr_safe_example.

As should be clear, mtr_safe_example_1 and mtr_safe_example_2 are threadsafe functions that you can registered as "RP$" when running Excel 2007 and "RP" when
running Excel 2003. You can create and register a version of this XLL function that uses an XLOPER12 as "UQ$" when running Excel 2007, but you cannot register it at all
in Excel 2003.

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 36/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007

Conclusion
This documentation is archived and is not being maintained.
This article discussed the development of XLLs in Office Excel 2007. The functionality described in this article is now available in the Excel 2007 XLL Software
Development Kit on the Microsoft Download Center.

About the Author


Steve Dalton is founder of Eigensys Ltd. in the UK. Eigensys works in the field of Excel development, specifically applied to financial analytics. Mr. Dalton is the author of
Excel Addin Development in C/C++: Applications in Finance Wiley, 2004 and Financial Applications Using Excel Addin Development in C/C++ Wiley, 2007.

This article was developed in partnership with A23 Consulting.

Additional Resources
To learn more about developing addins in Excel 2007, see the following resources:

Excel 2007 XLL Software Development Kit

How to Build an Addin XLL for Excel Using Visual C++

Book: Financial Applications Using Excel Addin Development in C/C++

Book: Excel Addin Development in C/C++: Applications in Finance

Visual Basic and the Excel Version 4.0 Macro Language

XLM Function Help: Macrofun.exe File Available on Online Services

Excel Developer Portal

Excel 2007 Resource Center

2017 Microsoft

https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 37/37

You might also like