What is a simple and reliable C library for working with Excel files? [closed]

Solution 1:

Strongly discouraged. I'd recommend using a C-friendly format (e.g. CSVs) instead of XLS, or using the new XML formats (take your pick on XML and ZIP libraries).

Still, for a quick fix, you could export to quoted CSV and then import using VBScript. Something like this, although I'd try to get it to work in VBA first.

Note that this will require a copy of Office, and will not scale well (but you can hide the Excel window).


I've just found xlsLib, so if you really need to write directly in C, give it a go! Be careful though, because it's very hard to get right, especially if you're writing to already-existing files.

There also exists LibExcel, but that's C++, so you'd need to compile a wrapper around, or rewrite for C.


One final caveat: the reason I didn't search for these at the start is that it's extremely difficult to get right. I have not used the libraries above, but I suspect they'll break in strange and unusual ways. I trust you've read Joel's take on the Office formats.

Solution 2:

Excel, like other Office products, exports its guts over COM. This is available for use in C++, VB, C#, and whatever other languages have COM interop -- provided that you're running in Windows and have Excel installed. (You can't get to COM from plain C. Whether this is fortunate or unfortunate is up to you.)

COM is a bloody messy pain for unmanaged languages, though. The following VB:

Set objExcel = CreateObject("Excel.Application")  ' start or use existing Excel
objExcel.Visible = True                           ' show the window
objExcel.Workbooks.Add                            ' create an empty workbook

roughly translates into the following C++:

#include <assert.h>
#include <ole2.h>
#include <tchar.h>

int main() {
    HRESULT hr;
    IDispatch *objExcel, *objWorkbooks;
    CLSID clsid;
    DISPID id, id2;
    DISPPARAMS p;
    VARIANT v;
    TCHAR *name;

    CoInitialize(NULL);

    hr = CLSIDFromProgID(_T("Excel.Application"), &clsid);
    assert(SUCCEEDED(hr));
    hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
            IID_IDispatch, (LPVOID *)&objExcel);
    assert(SUCCEEDED(hr));

    id2 = DISPID_PROPERTYPUT;
    name = _T("Visible");
    hr = objExcel->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &id);
    assert(SUCCEEDED(hr));
    VariantInit(&v);
    v.vt = VT_I4;
    v.lVal = 1;
    p.cArgs = 1;
    p.rgvarg = &v;
    p.cNamedArgs = 1;
    p.rgdispidNamedArgs = &id2;
    hr = objExcel->Invoke(id, IID_NULL, LOCALE_SYSTEM_DEFAULT,
            DISPATCH_PROPERTYPUT, &p, NULL, NULL, NULL);
    assert(SUCCEEDED(hr));

    name = _T("Workbooks");
    hr = objExcel->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &id);
    assert(SUCCEEDED(hr));
    p.cArgs = 0;
    p.rgvarg = NULL;
    p.cNamedArgs = 0;
    p.rgdispidNamedArgs = NULL;
    hr = objExcel->Invoke(id, IID_NULL, LOCALE_SYSTEM_DEFAULT,
            DISPATCH_PROPERTYGET, &p, &v, NULL, NULL);
    assert(SUCCEEDED(hr));
    objWorkbooks = v.pdispVal;

    name = _T("Add");
    hr = objWorkbooks->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &id);
    assert(SUCCEEDED(hr));
    p.cArgs = 0;
    p.rgvarg = NULL;
    p.cNamedArgs = 0;
    p.rgdispidNamedArgs = NULL;
    hr = objWorkbooks->Invoke(id, IID_NULL, LOCALE_SYSTEM_DEFAULT,
            DISPATCH_PROPERTYGET, &p, NULL, NULL, NULL);
    assert(SUCCEEDED(hr));

    return 0;
}