通過 COM 接口控制 Excel.Application,實現報表生成、數據導入導出。核心流程:初始化 COM → 創建 Excel 對象 → 獲取 Workbooks/Worksheets/Range → 讀寫數據 → 釋放資源退出。
Ctrl+W)。Add Class → From a type library。C:\Program Files\Microsoft Office\Office\Excel8.olb...Office\Excel9.olb...Office10/11\Excel.exe (內嵌類型庫)_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range。CApplication、CWorkbooks 等。
// Stdafx.h 或對話框頭文件
#include // _variant_t, _bstr_t
#include "excel.h" // 生成的包裝類頭文件
// 對話框類成員
CApplication m_excelApp; // Excel.Application
CWorkbooks m_books; // Workbooks 集合
CWorkbook m_book; // 單個 Workbook
CWorksheets m_sheets; // Worksheets 集合
CWorksheet m_sheet; // 單個 Worksheet
CRange m_range; // Range (單元格區域)
BOOL CMyDlg::OnInitDialog() {
CDialog::OnInitDialog();
// 1. 初始化 COM 庫
if (FAILED(::CoInitialize(NULL))) {
AfxMessageBox(_T("COM 初始化失敗"));
return FALSE;
}
// 2. 創建 Excel.Application 對象
if (!m_excelApp.CreateDispatch(_T("Excel.Application"))) {
AfxMessageBox(_T("無法啟動 Excel,請確認已安裝"));
::CoUninitialize();
return FALSE;
}
// 3. 可選:顯示/隱藏 Excel 窗口
m_excelApp.put_Visible(TRUE); // FALSE 後台運行
// 4. 獲取 Workbooks 集合
m_books.AttachDispatch(m_excelApp.GetWorkbooks(), TRUE);
// 5. 打開或新建工作簿
// 打開現有文件
m_book.AttachDispatch(m_books.Add(_variant_t(_T("C:\\Report.xls"))), TRUE);
// 或新建空白
// m_book.AttachDispatch(m_books.Add(vtMissing), TRUE);
// 6. 獲取工作表集合與第一張表
m_sheets.AttachDispatch(m_book.GetWorksheets(), TRUE);
m_sheet.AttachDispatch(m_sheets.GetItem(_variant_t(_T("Sheet1"))), TRUE);
// 7. 獲取 Cells 範圍對象 (用於單元格讀寫)
m_range.AttachDispatch(m_sheet.GetCells(), TRUE);
return TRUE;
}
// 寫入單元格 (行, 列 從 1 開始)
void WriteCell(long row, long col, const CString& value) {
_variant_t vRow(row), vCol(col), vVal(value);
m_range.SetItem(vRow, vCol, vVal);
}
// 讀取單元格
CString ReadCell(long row, long col) {
_variant_t vRow(row), vCol(col);
_variant_t result = m_range.GetItem(vRow, vCol);
if (result.vt == VT_BSTR)
return CString((LPCTSTR)(_bstr_t)result);
else if (result.vt == VT_R8) // 數值
return CString().Format(_T("%.2f"), result.dblVal);
return _T("");
}
// 批量寫入範圍 (高效)
void WriteRange(long r1, long c1, long r2, long c2, const COleSafeArray& data) {
CRange range;
range.AttachDispatch(m_sheet.GetRange(
_variant_t(_T("A1")), _variant_t(_T(""))), TRUE); // 需計算地址
range.SetValue(_variant_t(data));
}
_variant_t 與 _bstr_tVARIANT。_variant_t 封裝 VARIANT,支持隱式構造/轉換。_bstr_t 封裝 BSTR (COM 字符串),支持與 CString 互轉。vtMissing (定義於 afxdisp.h) 表示可選參數省略。
void CleanupExcel() {
// 1. 釋放 Range
if (m_range.m_lpDispatch) {
m_range.ReleaseDispatch();
}
// 2. 釋放 Worksheet
if (m_sheet.m_lpDispatch) {
m_sheet.ReleaseDispatch();
}
// 3. 釋放 Worksheets 集合
if (m_sheets.m_lpDispatch) {
m_sheets.ReleaseDispatch();
}
// 4. 關閉並釋放 Workbook
if (m_book.m_lpDispatch) {
m_book.Close(COleVariant((short)FALSE), // 不保存修改
COleVariant((short)FALSE),
vtMissing);
m_book.ReleaseDispatch();
}
// 5. 釋放 Workbooks 集合
if (m_books.m_lpDispatch) {
m_books.ReleaseDispatch(); // 無 Close 方法
}
// 6. 退出 Excel 應用程序
if (m_excelApp.m_lpDispatch) {
m_excelApp.Quit(); // 關鍵:否則進程殘留
m_excelApp.ReleaseDispatch();
}
// 7. 反初始化 COM
::CoUninitialize();
}
⚠️ 必須調用 Quit(),否則 Excel 進程將在後台殘留 (任務管理器可見 EXCEL.EXE)。
| 問題 | 原因 | 解決 |
|---|---|---|
| 編譯錯誤:找不到 excel.h | 類型庫導入路徑錯誤 | ClassWizard 重新導入,確認生成路徑 |
| 運行時 "類未註冊" | Excel 未安裝或版本不匹配 | 檢查註冊表 HKCR\Excel.Application\CLSID |
| 中文亂碼 | 字符集不匹配 | 項目設置 Unicode,用 _variant_t(CString) 自動轉 BSTR |
| 寫入數值顯示為科學計數法 | 單元格格式為常規 | 設置 Range.NumberFormat = _T("@") (文本) 或 0.00 |
| 大量數據寫入極慢 | 逐單元格 COM 呼叫開銷大 | 用 SafeArray 批量寫入 Range.Value = array |
void CMyDlg::OnExportExcel() {
// ... 初始化同上 ...
// 寫入表頭
WriteCell(1, 1, _T("ID"));
WriteCell(1, 2, _T("Name"));
WriteCell(1, 3, _T("Score"));
// 模擬數據
for (int i = 0; i < 100; i++) {
WriteCell(i+2, 1, CString().Format(_T("%d"), i+1));
WriteCell(i+2, 2, CString().Format(_T("User%d"), i+1));
WriteCell(i+2, 3, CString().Format(_T("%.1f"), 60.0 + rand()%40));
}
// 保存並可選打開
m_book.SaveAs(_variant_t(_T("C:\\Export.xls")), vtMissing, vtMissing, vtMissing,
vtMissing, vtMissing, 0, 1, vtMissing, vtMissing);
AfxMessageBox(_T("導出完成!"));
CleanupExcel();
}
MFC 操作 Excel 核心是 COM 自動化:CoInitialize → CreateDispatch → 獲取層級對象 → _variant_t 讀寫 → 逆序 ReleaseDispatch → Quit → CoUninitialize。關鍵點:類型庫正確導入、_variant_t 類型轉換、必須調用 Quit() 避免進程殘留、大數據用 SafeArray 批量操作。掌握此模式,可構建強大的 Office 文檔自動化功能。