Question

Currently I'm developing an Excel 2010 Add-In (.xll) with the Excel SDK 2010. Unfortunately there is little documentation from Microsoft (or perhaps I just haven't found it yet). I reached the point that my Add-In is loaded inside of Excel, its menu appears, and I'm able to invoke a function from the menu, which does get executed. Inside of it I try to gather all the Sheet names, but every call to the function results in a different result. The correct Sheet names are present in the returned XLOPER but they are surrounded by changing "weird" characters.

The code in question:

LPXLOPER12 GetWorkbook(void){
    LPXLOPER12 workbooksheets=new XLOPER12,xworkbookname = new XLOPER12;

    memset(xworkbookname,0,sizeof(XLOPER12));
    memset(workbooksheets,0,sizeof(XLOPER12));

    Excel12f(xlfGetDocument,xworkbookname,1,TempInt12(88));
    Excel12f(xlfGetWorkbook,workbooksheets,2,TempInt12(1),xworkbookname);

    // at this point I expect xworkbookname->val.str to contain the workbook name
    // but instead it has garbage before and after
    return 0;
}
Was it helpful?

Solution

You still haven't provided much information here, but I'm going to guess.

Since computers work with numbers, there has to be a convention for how text is represented. One convention, used by the C programming language, is to say "the first character of the text is in the first memory location, and the text continues until you hit a byte/word with the value of 0". Since you're writing in C, I guess that's what you're expecting.

But Microsoft didn't originally develop Excel in C. They developed in in Pascal. And the convention used by Pascal is "the first memory location contains the length of the text, and the actual text starts at the second memory location, going for as many memory locations as is specified by the length. There is no terminating 0."

There's actually quite a lot of documentation available on the MSDN site, for instance http://msdn.microsoft.com/en-us/library/aa730920%28v=office.12%29.aspx

In particular, they have a graphic (and sample code) showing what routines you need to use to convert between Pascal strings and C strings.

OTHER TIPS

As AnotherParker has alluded to, xlfGetWorkbook returns the Sheet name as a Length Prefixed string, so to use it in your function, you need to convert it to a Null Delimited string.

In addition, xlfGetWorkbook prefixes the Sheet name with the Workbook name in [] brackets, so you need to strip that out to get the actual Sheet name.

Here is a complete working example in C, without using a Framework of any flavour, which outputs the Sheet names in the first column of the Activesheet.

int __stdcall ListSheetsD (void)
{
    static XLOPER12 xWorkbook;
    XLOPER12        xRef, xNumParm, xAutoFit, xFalse, xlCell[10], xlArray;
    int             rc, i, iSheets;
    size_t          fullnamelen, sheetnamelen;
    wchar_t         *fullname, *sheetname;
    XCHAR           *ptr;
    OLECHAR         buffer[100];
    OLECHAR         *function = L"ListSheetsD";

    xNumParm.xltype = xltypeNum;
    xNumParm.val.num = 1;

    rc = Excel12 ( xlfGetWorkbook, (LPXLOPER12)&xWorkbook, 1, (LPXLOPER12)&xNumParm);
    if ( rc != xlretSuccess )
    {
        swprintf ( buffer, 100, L"xlfGetWorkbook. rc=%d", rc ); 
        MessageBox (NULL, buffer, function, MB_OK | MB_SETFOREGROUND );
    }

    //Get Number of Sheets
    iSheets = xWorkbook.val.array.columns;

    for ( i = 0; i < iSheets; i++ ) 
    {
        if ( i < 10 )
        {
            //Pickup Sheet Name (format: [Book.xls]Sheet1) & set it up as a Null Delimited String
            fullnamelen = xWorkbook.val.array.lparray[i].val.str[0];
            fullname = (wchar_t *) malloc ( (fullnamelen + 2) * sizeof (wchar_t) );
            memcpy (fullname, (xWorkbook.val.array.lparray[i].val.str + 1), 
                   (fullnamelen) * sizeof(wchar_t));
            fullname[fullnamelen] = L'\0';     

            //Extract Sheet Name (format: Sheet1) & set it up as a Length Prefixed String
            sheetname = (wchar_t *) malloc ( (fullnamelen + 2) * sizeof (wchar_t) );
            ptr = wcschr (fullname, L']');
            sheetnamelen = wcslen (ptr);
            wcscpy ( sheetname, ptr );
            sheetname[0] = sheetnamelen;

            //Setup Output Row
            xlCell[i].xltype = xltypeStr;
            xlCell[i].val.str = sheetname;

            free (fullname);
        }
    }

    if ( i > 10 ) i = 10;    

    xlArray.xltype = xltypeMulti;
    xlArray.val.array.rows = i;
    xlArray.val.array.columns = 1;
    xlArray.val.array.lparray = &xlCell[0];

    xRef.xltype = xltypeSRef;
    xRef.val.sref.count = 1;
    xRef.val.sref.ref.rwFirst = 0;
    xRef.val.sref.ref.rwLast = i - 1;
    xRef.val.sref.ref.colFirst = 0;
    xRef.val.sref.ref.colLast = 0;  

    rc = Excel12 ( xlSet, 0, 2, (LPXLOPER12)&xRef, (LPXLOPER12)&xlArray );
    if ( rc != xlretSuccess )
    {  
        swprintf ( buffer, 100, L"xlSet. Error=%d", rc );   
        MessageBox (NULL, buffer, function, MB_OK | MB_SETFOREGROUND );
    }

    rc = Excel12 (xlFree, NULL, 2, &xWorkbook, &xlArray );
    if ( rc != xlretSuccess )
    {
        swprintf ( buffer, 100, L"xlFree. rc=%d", rc ); 
        MessageBox (NULL, buffer, function, MB_OK | MB_SETFOREGROUND );
    }

    rc = Excel12 ( xlcSelect, 0, 1, (LPXLOPER12)&xRef );
    if ( rc != xlretSuccess )
    {
        swprintf ( buffer, 100, L"xlcSelect. rc=%d", rc );  
        MessageBox (NULL, buffer, function, MB_OK | MB_SETFOREGROUND );
    }

    xAutoFit.xltype = xltypeNum;
    xAutoFit.val.num = 3;

    xFalse.xltype = xltypeBool;  
    xFalse.val.xbool = 0;   

    rc = Excel12 ( xlcColumnWidth, 0, 4, &xNumParm, (LPXLOPER12)&xRef, &xFalse, &xAutoFit );
    if ( rc != xlretSuccess )
    {
        swprintf ( buffer, 100, L"xlcColumnWidth. rc=%d", rc ); 
        MessageBox (NULL, buffer, function, MB_OK | MB_SETFOREGROUND );
    }
    return 1;
}

Try http://xll.codeplex.com. It will make your life easier.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top