I have the source code below for an XLL Excel add-in. It compiles to an XLL without any problems, and Excel accepts it when I register it using the add-in manager(i.e. no "file not a valid add-in" error). But the problem is that it appears in the add-ins list with the description "xllSample"(the name of the XLL file as opposed to "Sample XLL Add-In"), and none of the functions in it are available.
The add-in source code is from a book. An XLL file compiled from the same source code is also provided besides the code, which works fine as an Add-in.
Can someone please point out what I am missing?
P.S. I am on a Windows7(x64) with Office 2013(32-bit) and using Excel 2013 XLL SDK. Compiler I'm using is VC 15.2.
P.S.S. There are a couple of points I have noticed that may or may not be relevant:
- Although the XLL file that comes with the book I mentioned works fine both on Office 2013(32-bit) on a x64 platform AND Office 2010(32-bit) on a x86 platform, the one I compile myself for x86 does NOT work on Windows7(x64) with Office 2013(32-bit)(i.e. I get the error "xllSample.xll is not a valid add-in"). When I compile the source for x64, it is accepted as an add-in by Excel, but I have the problem described above.
- I don't yet know how to enter compiler's debug mode for an XLL file but through indirect means I found out DllMain() is not called when I register the add-in.
This is the source code:
xllSample.cpp
#include <windows.h>
#include "xlcall.h"
#ifdef __cplusplus
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif
#define NUM_FUNCTIONS 3 // number of functions in the table
#define NUM_REGISTER_ARGS 11 // number of register arguments for each function
#define MAX_LENGTH 255 // maximum allowable string length for a register argument
static char gszFunctionTable[NUM_FUNCTIONS][NUM_REGISTER_ARGS][MAX_LENGTH] =
{
{" AddTwo", // procedure
" BBB", // type_text
" AddTwo", // function_text
" d1, d2", // argument_text
" 1", // macro_type
" Sample Add-In", // category
" ", // shortcut_text
" ", // help_topic
" Adds the two arguments.", // function_help
" The first number to add.", // argument_help1
" The second number to add." // argument_help2
},
{" MultiplyTwo",
" BBB",
" MultiplyTwo",
" d1, d2",
" 1",
" Sample Add-In",
" ",
" ",
" Multiplies the two arguments.",
" The first number to multiply.",
" The second number to multiply."
},
{" IFERROR",
" RRR",
" IFERROR",
" ToEvaluate, Default",
" 1",
" Sample Add-In",
" ",
" ",
" If the first argument is an error value, the second "
"argument is returned. Otherwise the first argument "
"is returned.",
" The argument to be checked for an error condition.",
" The value to return if the first argument is an error."
}
};
void HandleRegistration(BOOL);
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) {
return TRUE;
}
EXPORT int WINAPI xlAutoOpen(void) {
static XLOPER xDLL;
int i, j;
for(i = 0; i < NUM_FUNCTIONS; ++i)
for(j = 0; j < NUM_REGISTER_ARGS; ++j)
gszFunctionTable[i][j][0] =
(BYTE)strlen(gszFunctionTable[i][j] + 1);
HandleRegistration(TRUE);
return 1;
}
EXPORT int WINAPI xlAutoClose(void) {
HandleRegistration(FALSE);
return 1;
}
EXPORT LPXLOPER WINAPI xlAddInManagerInfo(LPXLOPER xlAction) {
static XLOPER xlReturn, xlLongName, xlTemp;
xlTemp.xltype = xltypeInt;
xlTemp.val.w = xltypeInt;
Excel4(xlCoerce, &xlReturn, 2, xlAction, &xlTemp);
if(1 == xlReturn.val.w) {
xlLongName.xltype = xltypeStr;
xlLongName.val.str = "\021Sample XLL Add-In";
} else {
xlLongName.xltype = xltypeErr;
xlLongName.val.err = xlerrValue;
}
return &xlLongName;
}
static void HandleRegistration(BOOL bRegister) {
XLOPER xlXLLName, xlRegID, xlRegArgs[NUM_REGISTER_ARGS];
int i, j;
Excel4(xlGetName, &xlXLLName, 0);
for(i = 0; i < NUM_REGISTER_ARGS; ++i)
xlRegArgs[i].xltype = xltypeStr;
for(i = 0; i < NUM_FUNCTIONS; ++i) {
for(j = 0; j < NUM_REGISTER_ARGS; ++j)
xlRegArgs[j].val.str = gszFunctionTable[i][j];
if(TRUE == bRegister) {
Excel4(xlfRegister, 0, NUM_REGISTER_ARGS + 1,
&xlXLLName,
&xlRegArgs[0], &xlRegArgs[1], &xlRegArgs[2],
&xlRegArgs[3], &xlRegArgs[4], &xlRegArgs[5],
&xlRegArgs[6], &xlRegArgs[7], &xlRegArgs[8],
&xlRegArgs[9], &xlRegArgs[10]);
} else {
xlRegArgs[4].val.str = "\0010";
Excel4(xlfRegister, 0, NUM_REGISTER_ARGS + 1,
&xlXLLName,
&xlRegArgs[0], &xlRegArgs[1], &xlRegArgs[2],
&xlRegArgs[3], &xlRegArgs[4], &xlRegArgs[5],
&xlRegArgs[6], &xlRegArgs[7], &xlRegArgs[8],
&xlRegArgs[9], &xlRegArgs[10]);
Excel4(xlfRegisterId, &xlRegID, 2, &xlXLLName,
&xlRegArgs[0]);
Excel4(xlfUnregister, 0, 1, &xlRegID);
}
}
Excel4(xlFree, 0, 1, &xlXLLName);
}
EXPORT double WINAPI AddTwo(double d1, double d2) {
return d1 + d2;
}
EXPORT double WINAPI MultiplyTwo(double d1, double d2) {
return d1 * d2;
}
EXPORT LPXLOPER IFERROR(LPXLOPER ToEvaluate, LPXLOPER Default) {
int IsError = 0;
XLOPER xlResult;
static XLOPER xlBadArgErr;
xlBadArgErr.xltype = xltypeErr;
xlBadArgErr.val.err = xlerrValue;
if((xltypeMissing == ToEvaluate->xltype) ||
(xltypeMissing == Default->xltype))
return &xlBadArgErr;
switch(ToEvaluate->xltype) {
case xltypeNum:
case xltypeStr:
case xltypeBool:
case xltypeInt:
break;
case xltypeSRef:
case xltypeRef:
if(xlretUncalced == Excel4(xlCoerce, &xlResult, 1, ToEvaluate))
return 0;
else {
if(xltypeMulti == xlResult.xltype)
return &xlBadArgErr;
else if(xltypeErr == xlResult.xltype)
IsError = 1;
}
Excel4(xlFree, 0, 1, &xlResult);
break;
case xltypeMulti:
return &xlBadArgErr;
break;
case xltypeErr:
IsError = 1;
break;
default:
return &xlBadArgErr;
break;
}
if(IsError)
return Default;
else
return ToEvaluate;
}