本類庫 CODBCExcel 封裝 MFC CDatabase/CRecordset,通過 ODBC 驅動實現對 Excel(.xls)的讀取、寫入、修改、工作表管理,無需安裝 Office,適合服務端自動化報表場景。
CODBCExcel:管理數據庫連接、工作表集合(map<CString, CODBCExcelSheet*>)CODBCExcelSheet:單工作表操作,內含 CRecordset、單元格緩存(vector<vector<CODBCExcelCell>>)、列標題、狀態標記CODBCExcelCell:單元格值存儲與類型轉換CString CODBCExcel::GetExcelDriver() {
TCHAR szBuf[2001]; WORD cbBufMax = 2000, cbBufOut;
if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut)) return "";
TCHAR* pszBuf = szBuf;
do {
if (_tcsstr(pszBuf, _T("Excel"))) return CString(pszBuf);
pszBuf = _tcschr(pszBuf, _T('\0')) + 1;
} while (pszBuf[1] != _T('\0'));
return "";
}
bool CODBCExcel::Open(const CString& strFileName) {
if (m_db.IsOpen()) return true;
CString strDSN;
strDSN.Format(_T("DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s"),
GetExcelDriver(), strFileName, strFileName);
return m_db.OpenEx(strDSN, CDatabase::noOdbcDialog) == TRUE;
}
關鍵點:FIRSTROWHASNAMES=1 將第一行作為列名;CREATE_DB 支持自動建文件。
CODBCExcelSheet* CODBCExcel::GetWorkSheet(const CString& strSheetName) {
CString strSQL;
strSQL.Format(_T("SELECT * FROM [%s$A1:IV65536]"), strSheetName); // 固定範圍
auto iter = m_Sheets.find(strSheetName);
CODBCExcelSheet* sheet = nullptr;
bool bNew = false;
if (iter != m_Sheets.end()) {
sheet = iter->second;
if (sheet->m_nFlag == CODBCExcelSheet::Add || sheet->m_nFlag == CODBCExcelSheet::Update)
return sheet; // 已在編輯狀態,直接返回
} else {
sheet = new CODBCExcelSheet(*this, strSheetName);
bNew = true;
}
try {
sheet->m_recordset.Open(CRecordset::forwardOnly, strSQL, CRecordset::readOnly);
} catch(...) {
if (sheet) sheet->m_recordset.Close();
if (!bNew) return sheet;
delete sheet; return nullptr;
}
sheet->Init(); // 讀取列標題、全部數據入緩存
m_Sheets.insert({strSheetName, sheet});
return sheet;
}
void CODBCExcelCell::Set(const CDBVariant& dbVal) {
switch(dbVal.m_dwType) {
case DBVT_SINGLE: {
long lVal = (long)dbVal.m_fltVal;
if (dbVal.m_fltVal != (float)lVal)
m_strVal.Format(_T("%f"), dbVal.m_fltVal);
else
m_strVal.Format(_T("%ld"), lVal); // 整數不顯示 .0
} break;
case DBVT_DOUBLE: {
long lVal = (long)dbVal.m_dblVal;
if (dbVal.m_dblVal != (double)lVal)
m_strVal.Format(_T("%f"), dbVal.m_dblVal);
else
m_strVal.Format(_T("%ld"), lVal);
} break;
// ... 其他類型 ...
}
}
這是 2009-11-02 修復的核心邏輯:浮點數若實為整數,按整數格式化,完美解決 Excel 顯示一致性問題。
bool CODBCExcelSheet::Save() {
switch(m_nFlag) {
case Update:
// DROP TABLE → CREATE TABLE → INSERT 逐行
case Add:
// CREATE TABLE → INSERT 逐行
case Delete:
// DROP TABLE
}
return true;
}
注意:Excel ODBC 驅動不支持 UPDATE 語句,只能刪表重建,故大量數據寫入較慢,適合中小規模報表。
CODBCExcelCell* CODBCExcelSheet::Cell(UINT rowIndex, UINT colIndex) {
if (rowIndex > 65535 || colIndex > 255) return nullptr;
if (colIndex >= m_dwCols) return nullptr;
if (rowIndex >= m_dwRows) {
m_dwRows++;
m_Cells.resize(m_dwRows);
for (auto& row : m_Cells) row.resize(m_dwCols);
// 設置 Parent 指針以便回寫標記 dirty
}
return &m_Cells[rowIndex][colIndex];
}
ExecuteSQL 調用開銷IV65536 限制為 Excel 2003 行數上限,.xlsx 需換 ACE 驅動try-catch(CDBException*),防止 MFC 異常拋出到調用層這套類庫展示了 MFC ODBC 封裝的最佳實踐:連接管理、記錄集緩存、類型智能轉換、工作表生命週期控制。雖然 Excel ODBC 有諸多限制(無 UPDATE、行數上限、性能一般),但在「無 Office 環境、服務端導出報表」場景下,它仍是輕量級、免依賴的首選方案。