How To Read Data From ODBC And Send It Into Excel Defined Names in C++

Peter Jones (BIDA) 151 Reputation points
2026-06-30T09:15:11.2933333+00:00

Hello C++ COM Experts,

We have developed a way of storing SQL in the cloud, reading it, resolving parameters, running it, fetching results, and sending the results into Excel PowerPivot and refreshing the charts. This is very useful in the world of BI/Reporting. But what we have done requires a third party vendor product so I won't mention it here. That's not fair.

While inventing all this it has occurred to me that for the simpler SQL / Reports we could just write a C++ program. We have mountains of C++ that read ODBC so that's no problem. I have been writing C++ for 25 years but I am not a hard core C++ programmer.

We have zero code writing to the Excel using COM and we believe that COM is the "recommended approach".

We got the code snippet below from co-pilot. When we imported EXCEL.EXE we get errors for unresolved references and so then we imported MSO.DLL. And it got unresolved references and co-pilot said to import VBE7EXT.OLE. But we can't find that file anywhere on any of our machines. So we are guessing that is a mistake.

When I re-create a new project to test some times the imports generate the Excel.tlh file etc and sometimes not. We have not been able to figure out how to reliably create a project in VS 2026 and get this code snippet to compile. We can't even get it to reliably generate the tlh files. We are creating a C++ windows console application at the beginning because that's what co-pilot said to create for this code snippet.

We have searched all over for examples and videos and that sort of thing. Amazingly to us we can't find any reliable detailed instructions on how to go about getting C++ to read/write to Excel via the COM model. (Or another seemingly recommended way.) I don't know if we are just missing something obvious because I would have thought that lots of people are doing this because it's useful. Everything we find is pretty much a repeat of the code snippet below.

If anyone knows of any post or video or reliable source that shows exactly how this should work? I would really appreciate the link please.

I don't know what the cause of these problems of unresolved references and not generating the tlh files is. I have tried "fresh installs" of VS 2026. When the tls files do get generated we don't know why and how, they just suddenly appear and then go away again after a clean. We have tried cleaning and rebuilding. I have pretty much spent about 12 hours trying to get this piece of code to compile and I just can't figure out why it's not working. So if you have any pointers I would very much appreciate them.

Thank you in advance for any assistance you may be able to offer me.

Best Regards
Peter Jones.

#include <iostream>

#include <vector>

#include <string>

#include <windows.h>

#include <sqlext.h>

#include <comdef.h>

//VBA COM

#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA7.1\VBE7EXT.OLE" \

rename("Reference", "VBReference") \

rename("References", "VBReferences")   
```// MSO COM

#import "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE16\\MSO.DLL" \

```javascript
rename("RGB", "MSORGB") \

rename("DocumentProperties", "MSODocumentProperties")
```// Excel COM

#import "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE" \

```r
rename("DialogBox", "ExcelDialogBox") \

rename("RGB", "ExcelRGB") \

exclude("IFont", "IPicture") \

rename("CopyFile", "ExcelCopyFile") \

rename("ReplaceText", "ExcelReplaceText") \

named_guids \

auto_rename
```using namespace Excel;

// =======================

// SQL SERVER (ODBC PART)

// =======================

std::vector<std::vector<std::wstring>> FetchDataFromSQL() {

```cpp
SQLHENV hEnv;

SQLHDBC hDbc;

SQLHSTMT hStmt;

std::vector<std::vector<std::wstring>> result;

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);

SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);

SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);

// ✅ Adjust connection string

SQLWCHAR connStr[] =

    L"DRIVER={ODBC Driver 17 for SQL Server};"

    L"SERVER=localhost;"

    L"DATABASE=TestDB;"

    L"Trusted_Connection=yes;";

SQLDriverConnect(hDbc, NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);

SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

// ✅ Query

SQLExecDirect(hStmt, (SQLWCHAR*)L"SELECT TOP 10 Name, Value FROM MyTable", SQL_NTS);

SQLWCHAR col1[256], col2[256];

while (SQLFetch(hStmt) == SQL_SUCCESS) {

    SQLGetData(hStmt, 1, SQL_C_WCHAR, col1, sizeof(col1), NULL);

    SQLGetData(hStmt, 2, SQL_C_WCHAR, col2, sizeof(col2), NULL);

    result.push_back({ col1, col2 });

}

SQLFreeHandle(SQL_HANDLE_STMT, hStmt);

SQLDisconnect(hDbc);

SQLFreeHandle(SQL_HANDLE_DBC, hDbc);

SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

return result;
```}

// =======================

// EXCEL AUTOMATION PART

// =======================

void PushToExcelAndRefresh(const std::vector<std::vector<std::wstring>>& data) {

```cpp
CoInitialize(NULL);

try {

    _ApplicationPtr app;

    app.CreateInstance(L"Excel.Application");

    app->Visible = VARIANT_FALSE;

    _WorkbookPtr wb = app->Workbooks->Open(L"C:\\temp\\Sample.xlsx");

    // ✅ Named range

    RangePtr range = wb->Names->Item("InputData")->RefersToRange;

    long rows = (long)data.size();

    long cols = (long)data[0].size();

    // ✅ Build SAFEARRAY

    SAFEARRAYBOUND bounds[2];

    bounds[0].lLbound = 1; bounds[0].cElements = rows;

    bounds[1].lLbound = 1; bounds[1].cElements = cols;

    SAFEARRAY* sa = SafeArrayCreate(VT_VARIANT, 2, bounds);

    long idx[2];

    for (long i = 0; i < rows; i++) {

        for (long j = 0; j < cols; j++) {

            idx[0] = i + 1;

            idx[1] = j + 1;

            VARIANT val;

            VariantInit(&val);

            val.vt = VT_BSTR;

            val.bstrVal = SysAllocString(data[i][j].c_str());

            SafeArrayPutElement(sa, idx, &val);

        }

    }

    // ✅ Push data into Excel

    range->Value2 = _variant_t(sa);

    std::cout << "Data written to Excel.\n";

    // ✅ Refresh everything

    wb->RefreshAll();

    // ✅ Wait for async queries (Power Query)

    app->CalculateUntilAsyncQueriesDone();

    // ✅ Explicit Power Pivot refresh (if available)

    try {

        wb->Model->Refresh();

    }

    catch (...) {

        std::cout << "Model refresh not supported.\n";

    }

    wb->Save();

    wb->Close(VARIANT_FALSE);

    app->Quit();

    std::cout << "Excel refresh complete.\n";

}

catch (_com_error& e) {

    std::wcout << "COM Error: " << e.ErrorMessage() << std::endl;

}

CoUninitialize();
```}

// =======================

// MAIN PIPELINE

// =======================

int main() {

```cpp
std::cout << "Fetching data from SQL Server...\n";

auto dataset = FetchDataFromSQL();

if (dataset.empty()) {

    std::cout << "No data returned.\n";

    return 0;

}

std::cout << "Pushing data to Excel...\n";

PushToExcelAndRefresh(dataset);

std::cout << "Pipeline complete.\n";

return 0;
```}
Developer technologies | C++
Developer technologies | C++

A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.


2 answers

Sort by: Most helpful
  1. Taki Ly (WICLOUD CORPORATION) 2,480 Reputation points Microsoft External Staff Moderator
    2026-07-01T07:34:25.8033333+00:00

    Hello @Peter Jones (BIDA) ,

    Following Bruce's idea, you may want to try the Open XML SDK from C++/CLI. I tested a small project and it built and ran fine, with no COM and no #import problems.

    Setup that worked for me:

    • A C++/CLI console project with CLR support on (/clr), targeting .NET Framework 4.8.
    • References: System, System.Data (for ODBC), WindowsBase, and DocumentFormat.OpenXml.dll (version 2.20.0, the net46 build, which needs no extra packages).

    The concept is simple. You read rows from ODBC, stream them into the sheet with OpenXmlWriter, then add the defined name at the end. Below is the short version:

    // read from ODBC
    OdbcConnection^ conn = gcnew OdbcConnection("DSN=YourDSN;");
    conn->Open();
    OdbcDataReader^ reader =
        (gcnew OdbcCommand("SELECT Name, Value FROM YourTable", conn))->ExecuteReader();
    // create workbook and stream one sheet
    SpreadsheetDocument^ doc =
        SpreadsheetDocument::Create("output.xlsx", SpreadsheetDocumentType::Workbook);
    WorkbookPart^ wbPart = doc->AddWorkbookPart();
    wbPart->Workbook = gcnew Workbook();
    WorksheetPart^ wsPart = wbPart->AddNewPart<WorksheetPart^>();
    OpenXmlWriter^ writer = OpenXmlWriter::Create(wsPart);
    writer->WriteStartElement(gcnew Worksheet());
    writer->WriteStartElement(gcnew SheetData());
    int row = 1;
    while (reader->Read())
    {
        // write A{row} and B{row} as inline string cells here
        row++;
    }
    writer->WriteEndElement(); // SheetData
    writer->WriteEndElement(); // Worksheet
    writer->Close();
    // register the sheet
    Sheets^ sheets = wbPart->Workbook->AppendChild(gcnew Sheets());
    Sheet^ sheet = gcnew Sheet();
    sheet->Id = wbPart->GetIdOfPart(wsPart);
    sheet->SheetId = gcnew UInt32Value((UInt32)1);
    sheet->Name = "Data";
    sheets->AppendChild(sheet);
    // add the defined name
    DefinedNames^ names = gcnew DefinedNames();
    DefinedName^ dn = gcnew DefinedName();
    dn->Name = "MyDataRange";
    dn->Text = "Data!$A$1:$B$" + (row - 1).ToString();
    names->AppendChild(dn);
    wbPart->Workbook->AppendChild(names);
    wbPart->Workbook->Save();
    delete doc; // this calls Dispose in C++/CLI
    

    A few small notes in case you see the same errors:

    • Text is ambiguous, so write it in full as DocumentFormat::OpenXml::Spreadsheet::Text.
    • Use gcnew UInt32Value(...) for SheetId, not a plain cast.
    • Use AppendChild(...). In C++/CLI, Append(oneItem) did not add anything for me and left the sheet empty.
    • Close with delete doc;.

    One point to keep in mind. Open XML only writes the file. It does not have a calculation or refresh engine. So the RefreshAll and Power Pivot Model->Refresh parts of your first snippet have no equal here. For simple reports this is fine. If you really need a Power Pivot refresh, that part still needs Excel automation or Office Scripts.

    Some links that may help:

    I hope some of this is helpful. If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation.

    Thank you.

    Was this answer helpful?


  2. Bruce (SqlWork.com) 84,251 Reputation points
    2026-06-30T15:03:38.98+00:00

    I believe you are on the wrong approach. There are several limitations mod using Office automation to access Excel.

    • Not supported to run on server
    • Requires a user profile, and open window to run
    • Its largely being deprecated
    • Requires Office be installed on the hosting machine.
    • Reliability issues in continuous use.

    a better approach would be to use Open Xml SDK to read and write Excel files. The main issue is that this SDK requires the .net runtime, so you must use C++/CLI so the C++ code can call the .net SDK

    https://learn.microsoft.com/en-us/office/open-xml/open-xml-sdk

    note: there are also commercial ODBC drivers. the Microsoft driver is out of support.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.