Developing Add-Ins (XLLS) in Excel 2007
Developing Add-Ins (XLLS) in Excel 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
October 2006
Applies to: 2007 Microsoft Office System, Microsoft Office Excel 2007
Contents
Overview of XLLs
Conclusion
Additional Resources
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++.
The XLOPER data structure and other Excel data types, such as the floatingpoint matrix structure FP.
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.
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
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.
Double B E
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 4/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007
This documentation
[signed] short [int] is Iarchived and is not
M being maintained. 16bit
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.
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:
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.
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.
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 6/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007
This
Max documentation
length: 255 extendedis ASCII
archived
bytesandMaximum
is not being maintained.
length 32,767 Unicode chars
Important
Nullterminated char * "C" Nullterminated wchar_t * "C%" Maximum length 32,767 Unicode chars
Max length: 255 extended ASCII bytes
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.
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 7/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007
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.
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;
}
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.
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.
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 9/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007
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.
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 11/37
4/5/2017 DevelopingAddins(XLLs)inExcel2007
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
xlcVbaEditWatch xlcVbaStepOver
xlcVbaEnd xlcVbaToggleBreakpoint
xlcVbaInstantWatch
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.
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.
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.
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#
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.
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.
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.
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
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.
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.
Returns specific errors if the inputs are not what they should be.
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#
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.
C#
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;
}
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#
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.
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.
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.
Call functions such as xlfGetCell, xlfGetWindow, xlfGetWorkbook, and xlfGetWorkspace and other information functions.
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.
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
INDIRECT
GETPIVOTDATA
CUBEMEMBER
CUBEVALUE
CUBEMEMBERPROPERTY
CUBESET
CUBERANKEDMEMBER
CUBEKPIMEMBER
CUBESETCOUNT
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
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.
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.
Additional Resources
To learn more about developing addins in Excel 2007, see the following resources:
2017 Microsoft
https://msdn.microsoft.com/enus/library/aa730920(v=office.12).aspx 37/37