VB 操作 Excel 極簡:引用類型庫 → CreateObject → 直接調用屬性/方法。VC 需手動處理 IDispatch::Invoke、參數封裝 DISPPARAMS、錯誤碼檢查,代碼量級差異。MFC 提供 COleDispatchDriver 封裝 IDispatch,並通過 ClassWizard 生成包裝類,大幅降低難度。
Invoke 詳解
HRESULT Invoke(
DISPID dispIdMember, // 成員標識符 (由 GetIDsOfNames 獲取)
REFIID riid, // 保留 IID_NULL
LCID lcid, // 語言環境
WORD wFlags, // 調用類型
DISPPARAMS *pDispParams, // 參數數組
VARIANT *pVarResult, // 返回值
EXCEPINFO *pExcepInfo, // 異常信息
UINT *puArgErr // 錯誤參數索引
);
wFlags 決定操作類型:
DISPATCH_METHOD:調用方法DISPATCH_PROPERTYGET:讀屬性DISPATCH_PROPERTYPUT:寫屬性MFC COleDispatchDriver 將其封裝為三個方法:
InvokeHelper(dispid, DISPATCH_METHOD, vtReturn, &result, params...)GetProperty(dispid, vtReturn, &result)SetProperty(dispid, vtType, value)Ctrl+W → Add Class → From a type libraryExcel8.olb (或對應版本)。_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range。CApplication、CWorkbooks、CWorkbook、CWorksheets、CWorksheet、CRange 等,繼承自 COleDispatchDriver。生成的類可能不完全符合需求 (如可選參數處理)。利用 C++ 重載擴展:
// 原生生成的 Workbooks::Add 可能包含 Template 參數
// 我們添加無參重載
class CWorkbooks : public COleDispatchDriver {
public:
// 原有聲明 (由向導生成)
LPDISPATCH Add(const VARIANT& Template);
// 自定義重載:無參新建
LPDISPATCH Add() {
return Add(vtMissing); // 傳遞 vtMissing 表示省略可選參數
}
};
同理可為 Worksheet::Range、Range::Item 等添加便捷重載。
// Stdafx.h
#include // MFC OLE 支持
// App::InitInstance
BOOL CMyApp::InitInstance() {
if (!AfxOleInit()) { // 初始化 OLE 庫
AfxMessageBox(_T("OLE 初始化失敗"));
return FALSE;
}
// ...
}
// 對話框按鈕處理
void CMyDlg::OnAutomateExcel() {
CApplication app;
if (!app.CreateDispatch(_T("Excel.Application"))) {
AfxMessageBox(_T("啟動 Excel 失敗"));
return;
}
app.put_Visible(TRUE);
CWorkbooks books;
books.AttachDispatch(app.GetWorkbooks(), TRUE);
CWorkbook book;
book.AttachDispatch(books.Add(), TRUE); // 使用重載無參 Add()
CWorksheets sheets;
sheets.AttachDispatch(book.GetWorksheets(), TRUE);
CWorksheet sheet;
sheet.AttachDispatch(sheets.GetItem(COleVariant((short)1)), TRUE); // 索引從1開始
CRange range;
range.AttachDispatch(sheet.GetCells(), TRUE);
// 寫入 A1
range.SetItem(COleVariant((long)1), COleVariant((long)1), COleVariant(_T("VC自動化測試")));
// 讀取 A1
COleVariant val = range.GetItem(COleVariant((long)1), COleVariant((long)1));
CString str = val.bstrVal; // 需檢查 vt == VT_BSTR
AfxMessageBox(_T("A1 = ") + str);
// 清理 (逆序)
range.ReleaseDispatch();
sheet.ReleaseDispatch();
sheets.ReleaseDispatch();
book.Close(COleVariant((short)FALSE), COleVariant((short)FALSE), vtMissing);
book.ReleaseDispatch();
books.ReleaseDispatch();
app.Quit();
app.ReleaseDispatch();
}
| 技術點 | 說明 |
|---|---|
AfxOleInit() | 應用程序啟動時調用一次,初始化 OLE/COM 庫 |
CreateDispatch | 連接到運行中的實例或啟動新實例 (ProgID: "Excel.Application") |
AttachDispatch(ptr, TRUE) | 包裝類接管 LPDISPATCH,TRUE 表示釋放時自動 Release |
COleVariant | MFC 封裝 VARIANT,支持多類型構造 |
vtMissing | 可選參數佔位符,定義於 afxdisp.h |
| 索引基數 | Excel COM 集合索引從 1 開始 (非 0) |
| 釋放順序 | 子對象 → 父對象 → Quit() → ReleaseDispatch |
若需響應 Excel 事件 (如 WorkbookBeforeClose),需實現 IDispatch 事件接口並通過 Advise 連接。MFC 提供 CCmdTarget 派生類支持連接點,但較複雜,常見需求通常改用輪詢或定時器檢查狀態。
AfxOleInit() → CreateDispatch 失敗。GetItem(0) 失敗/取錯數據。app.Quit() → Excel 進程殘留 (後台幽靈進程)。SetItem → 極慢 (秒級/分級)。用 SafeArray 批量賦值 Range.Value = array。MFC 通過 COleDispatchDriver 封裝 IDispatch::Invoke 復雜性,ClassWizard 生成包裝類進一步降低門檻。關鍵掌握:OLE 初始化、ProgID 創建、層級對象獲取 (App→Books→Book→Sheets→Sheet→Range)、COleVariant 參數傳遞、索引從 1 開始、逆序釋放與 Quit()。定制包裝類重載可選參數,大幅提升開發效率。大數據場景必用 SafeArray 批量操作。