1

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;
}
Kemal
  • 849
  • 5
  • 21
  • 1
    It is very weird problem... If you use VS , you can debug your xll by following the procedure described here : https://stackoverflow.com/a/33704781/3205529 . Then just place a breakpoint and press F5 – Malick Aug 25 '17 at 10:17
  • Did you succeed to debug your xll ? – Malick Aug 25 '17 at 13:30
  • 1
    @Malick : I appreciate the link. Very helpful. Stepping through the code does not always indicate the correct line in functions, though. Is there some way to correct that? – Kemal Aug 25 '17 at 14:47
  • 1
    It should be the correct line if you are building in Debug Mode. Please be sure that the xll that is loaded by excel is the one you built. remove all references to **ALL** xll in Excel, no need to add the xll via Excel if you follow the above link. You can place your breakpoint in xlAutoOpen. I suspect that the xll loaded is not the one you believe and this would explain your problem. – Malick Aug 25 '17 at 14:54
  • @Malick : I did find the problem at 04:00 in the morning. I wasn't using a .def file to export the function names, thinking it is not needed since `__declspec(dllexport)` is used with all exported functions. After some reading in MSDN on that issue, I have to say I am still very confused about that. From what I understand so far, It still seems to me that it *should* work fine *without* the .def file(i.e. function names *should* be exported **un**decorated). But when I look at the internal function names, they *are* decorated, so a .def file seems necessary. Am I missing something on that? – Kemal Aug 25 '17 at 15:07
  • @Malick : However I still have no idea why x64 build is accepted(though usable) by add-in manager, but x86 is not. I suspect it has *something* to do with function name decoration, as C functions(or C++ functions with `extern "C"` specifier) are not decorated in x64 builds, whereas they *are* decorated in x86 builds. – Kemal Aug 25 '17 at 15:09
  • 1
    See my post here : https://stackoverflow.com/questions/538134/exporting-functions-from-a-dll-with-dllexport/41910450#41910450 . Remove all your 'Export' and .def file and replace it with the #pragma EXPORT as explained in the link. There is a pb with the WINAPI macro and exports. – Malick Aug 25 '17 at 15:14
  • 1
    @Malick : Your suspicion was correct about the debugger. The .xll that ran in excel was not the one built in debug mode, so the debugger got confused. Thanks once again, your help is highly appreciated. – Kemal Aug 25 '17 at 15:42

0 Answers0