AutoXL.c

The code in autoxl.c actually creates the pie chart using the code in dispargs.c as helper functions to set up the Invoke function calls.

The CLSID for Microsoft Excel is unique and is used by OLE to identify the server to start. Instead of hardcoding the CLSID into your application, it is a good idea to obtain the CLSID from the registry using the ProgID. The ProgID for Microsoft Excel is "Excel.Application." The CLSIDFromProgID function looks up the ProgID in the registry and returns the associated CLSID.

CLSIDFromProgID(L"Excel.Application", &clsExcelApp);

Excel Instance-Management Functions

The StartExcel, ReleaseExcel, IsExcelRunning, and SetExcelVisible functions are used to manage an instance of Microsoft Excel. The StartExcel function uses the CoCreateInstance function to create an instance of Excel. The ReleaseExcel function invokes the Quit method for the Microsoft Excel Application object (this causes Excel to exit), and shuts down the OLE server. The SetExcelVisible function invokes the Visible method for the Application object, making the instance of Microsoft Excel visible.

IDispatch * pdispExcelApp = NULL;

BOOL StartExcel()
{
    CLSID clsExcelApp;

    // if Excel is already running, return with current instance
    if (pdispExcelApp != NULL)
        return TRUE;

    // Obtain the CLSID that identifies EXCEL.APPLICATION
    // This value is universally unique to Excel versions 5 and up, 
    // and is used by OLE to identify which server to start. We are 
    // obtaining the CLSID from the ProgID.

    if (FAILED(CLSIDFromProgID(L"Excel.Application", &clsExcelApp))) 
    {
        MessageBox(g_hwndApp, L"Cannot obtain CLSID from ProgID", 
            g_szAppTitle, MB_OK | MB_ICONSTOP);
        return FALSE;
    }

    // start a new copy of Excel, grab the IDispatch interface
    if (FAILED(CoCreateInstance(&clsExcelApp, NULL, CLSCTX_LOCAL_SERVER,
            &IID_IDispatch, &pdispExcelApp))) 
    {
        MessageBox(g_hwndApp, L"Cannot start an instance of Excel for 
            Automation.", g_szAppTitle, MB_OK | MB_ICONSTOP);
        return FALSE;
    }

    return TRUE;
}


BOOL ReleaseExcel()
{
    if (pdispExcelApp == NULL)
        return TRUE;
    
    // Tell Excel to quit, since for automation simply releasing the 
    // IDispatch object isn't enough to get the server to shut down.

    // Note that this code will hang if Excel tries to display any 
    // message boxes. This can occur if a document is in need of saving.
    // The CreateChart() code always clears the dirty bit on the 
    // documents it creates, avoiding this problem.

    ClearAllArgs();
    Invoke(pdispExcelApp, L"Quit", NULL, DISPATCH_METHOD, 0);

    // Even though Excel has been told to Quit, we still need to release 
    // the OLE object to account for all memory.

    (*(pdispExcelApp->lpVtbl->Release))(pdispExcelApp);

    pdispExcelApp = NULL;
    return TRUE;
}

BOOL IsExcelRunning()
{
    return pdispExcelApp != NULL;
}


BOOL SetExcelVisible(BOOL fVisible)
{
    if (!IsExcelRunning())
        return FALSE;

    ClearAllArgs();
    AddArgumentBool(NULL, 0, fVisible);
    return Invoke(pdispExcelApp, L"Visible", NULL, DISPATCH_PROPERTYPUT, 
        DISP_FREEARGS);
}

SetRangeValueDouble Function

The SetRangeValueDouble function uses PropertyPut to set the value of a range given the cell reference as a string. The CreateChart function calls this function several times to set cell values.

BOOL SetRangeValueDouble(IDispatch *pdispWs, LPOLESTR lpszRef, double d)
{
    VARIANTARG vargRng;
    BOOL fResult;

    ClearAllArgs();
    AddArgumentCString(NULL, 0, lpszRef);
    if (!Invoke(pdispWs, L"Range", &vargRng, DISPATCH_PROPERTYGET, 
        DISP_FREEARGS))
        return FALSE;

    AddArgumentDouble(NULL, 0, d);
    fResult = Invoke(vargRng.pdispVal, L"Value", NULL, 
        DISPATCH_PROPERTYPUT, 0);
    ReleaseVariant(&vargRng);

    return fResult;
}

CreateChart Function

The CreateChart function uses the argument-constructor functions and the Invoke function to create a pie chart in Microsoft Excel. The Visual Basic equivalent calls are described in the comments immediately preceding the AddArgumentType and Invoke function calls.

BOOL CreateChart()
{
    BOOL fResult;
    VARIANTARG varg1, varg2;
    IDispatch *pdispWorkbook = NULL;
    IDispatch *pdispWorksheet = NULL;
    IDispatch *pdispRange = NULL;
    IDispatch *pdispCrt = NULL;
    LPOLESTR apszNames[4] = { L"North", L"South", L"East", L"West" };

    // Set wb = [application].Workbooks.Add(template := xlWorksheet)
    ClearAllArgs();
    if (!Invoke(pdispExcelApp, L"Workbooks", &varg1, 
            DISPATCH_PROPERTYGET, 0))
        return FALSE;

    ClearAllArgs();
    AddArgumentInt2(L"Template", 0, xlWorksheet);
    fResult = Invoke(varg1.pdispVal, L"Add", &varg2, 
        DISPATCH_METHOD, 0);
    ReleaseVariant(&varg1);
    if (!fResult)
        return FALSE;
    pdispWorkbook = varg2.pdispVal;

    // Set ws = wb.Worksheets(1)
    ClearAllArgs();
    AddArgumentInt2(NULL, 0, 1);
    if (!Invoke(pdispWorkbook, L"Worksheets", &varg2, 
            DISPATCH_PROPERTYGET, 0))
        goto CreateChartBail;
    pdispWorksheet = varg2.pdispVal;

    // set up the data labels

    // ws.Range("A1:D1").Value = Array("North", "South", "East", "West")
    ClearAllArgs();
    AddArgumentCString(NULL, 0, L"A1:D1");
    if (!Invoke(pdispWorksheet, L"Range", &varg2, DISPATCH_PROPERTYGET, 
            DISP_FREEARGS))
        goto CreateChartBail;

    AddArgumentCStringArray(NULL, 0, apszNames, 4);
    fResult = Invoke(varg2.pdispVal, L"Value", NULL, 
        DISPATCH_PROPERTYPUT, DISP_FREEARGS);
    ReleaseVariant(&varg2);
    if (!fResult)
        goto CreateChartBail;

    // set up the data series values

    // ws.Range("A2") = 5.2
    if (!SetRangeValueDouble(pdispWorksheet, L"A2", 5.2))
        goto CreateChartBail;
    // ws.Range("B2") = 10
    if (!SetRangeValueDouble(pdispWorksheet, L"B2", 10.0))
        goto CreateChartBail;
    // ws.Range("C2") = 8
    if (!SetRangeValueDouble(pdispWorksheet, L"C2", 8.0))
        goto CreateChartBail;
    // ws.Range("D2") = 20
    if (!SetRangeValueDouble(pdispWorksheet, L"D2", 20))
        goto CreateChartBail;
    
    // set sourceRange = ws.Range("A1:D2")
    ClearAllArgs();
    AddArgumentCString(NULL, 0, L"A1:D2");
    if (!Invoke(pdispWorksheet, L"Range", &varg2, DISPATCH_PROPERTYGET, 
            DISP_FREEARGS))
        goto CreateChartBail;
    pdispRange = varg2.pdispVal;

    // set crt = wb.Charts.Add
    ClearAllArgs();
    if (!Invoke(pdispWorkbook, L"Charts", &varg1, 
            DISPATCH_PROPERTYGET, 0))
        goto CreateChartBail;
    ClearAllArgs();
    fResult = Invoke(varg1.pdispVal, L"Add", &varg2, 
        DISPATCH_METHOD, 0);
    ReleaseVariant(&varg1);
    if (!fResult)
        goto CreateChartBail;
    pdispCrt = varg2.pdispVal;

    // crt.ChartWizard source := sourceRange, gallery := xl3DPie, _
    // format := 7,     plotBy := xlRows, categoryLabels := 1, _
    // seriesLabels := 0, hasLegend := 2, title := "Sales Percentages"
    ClearAllArgs();
    AddArgumentCString(L"title", 0, L"Sales Percentages");
    AddArgumentInt2(L"hasLegend", 0, 2);
    AddArgumentInt2(L"seriesLabels", 0, 0);
    AddArgumentInt2(L"categoryLabels", 0, 1);
    AddArgumentInt2(L"plotBy", 0, xlRows);
    AddArgumentInt2(L"format", 0, 7);
    AddArgumentInt2(L"gallery", 0, xl3DPie);
    AddArgumentDispatch(L"source", 0, pdispRange);    // will auto-free
    pdispRange = NULL;
    if (!Invoke(pdispCrt, L"ChartWizard", NULL, DISPATCH_METHOD, 
            DISP_FREEARGS))
        goto CreateChartBail;

    // wb.Saved = True
    // ' So that Excel won't ask whether to save this document on close.
    ClearAllArgs();
    AddArgumentBool(NULL, 0, TRUE);
    Invoke(pdispWorkbook, L"Saved", NULL, DISPATCH_PROPERTYPUT, 0);

    fResult = TRUE;

CreateChartExit:
    if (pdispWorkbook != NULL)
        (*(pdispWorkbook->lpVtbl->Release))(pdispWorkbook);
    if (pdispWorksheet != NULL)
        (*(pdispWorksheet->lpVtbl->Release))(pdispWorksheet);
    if (pdispRange != NULL)
        (*(pdispRange->lpVtbl->Release))(pdispRange);
    if (pdispCrt != NULL)
        (*(pdispCrt->lpVtbl->Release))(pdispCrt);
    return fResult;

CreateChartBail:
    fResult = FALSE;
    goto CreateChartExit;
}