本文轉載於http://blog.sina.com.cn/s/blog_4c79cc450100ll43.html
最近發現很多人都在研究OFFICE方面的編程,當然,偶也是一個啦:) 可是這方面的資料卻很難找,而且大部分(幾乎全部)都是英文的。 於是,便有了寫這篇文章的念頭(好了,言歸正傳)。 本來OFFICE已經為大家提供了很好用的COM組件,但我發現我怎么用怎么不順手(估計是本人太菜了)。 於是便絞盡腦汁想用純 C++ 代碼來實現,終於,哈哈,嘿嘿,嚯嚯…… 好了,下面是我的步驟(偶用的VC++ 6.0): 1. 先新建一個“Win32 控制台應用/Win32 Console Application”工程,工程名不妨叫做“createXLS”。 2. 工程向導里選擇“A "Hello,World!" application”,新建完畢(廢話)。 3. 打開“createXLS.cpp”文件,添加代碼(本不想貼代碼的,想做個工程讓大家下載,因為太簡單,不好意思興師動眾了): #include <ole2.h> // 這個頭文件一定要包含,否則就不能自動化了 // 接着修改我們添加一個函數,這個函數是整個程序的基礎 // 若以后寫別的程序而想用純 C++ 來實現自動化,這個函數是可以復用的</span> HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...); // 修改主函數 int main(int argc, char* argv[]) { // printf("Hello World!\n"); // 注釋掉這一句 // 初始化COM庫 CoInitialize(NULL); // 獲得EXCEL的CLSID CLSID clsid; HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid); if(FAILED(hr)) { ::MessageBox(NULL, "CLSIDFromProgID() 函數調用失敗!", "錯誤", 0x10010); return -1; } // 創建實例 IDispatch *pXlApp; hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pXlApp); if(FAILED(hr)) { ::MessageBox(NULL, "請檢查是否已經安裝EXCEL!", "錯誤", 0x10010); return -2; } // 顯示,將Application.Visible屬性置1 VARIANT x; x.vt = VT_I4; x.lVal = 1; AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"Visible", 1, x); // 獲取Workbooks集合 IDispatch *pXlBooks; { VARIANT result; VariantInit(&result); AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"Workbooks", 0); pXlBooks = result.pdispVal; } // 調用Workbooks.Add()方法,創建一個新的Workbook IDispatch *pXlBook; { VARIANT result; VariantInit(&result); AutoWrap(DISPATCH_PROPERTYGET, &result, pXlBooks, L"Add", 0); pXlBook = result.pdispVal; } // 創建一個15x15的數組,用於填充表格 VARIANT arr; WCHAR szTmp[32]; arr.vt = VT_ARRAY | VT_VARIANT; SAFEARRAYBOUND sab[2]; sab[0].lLbound = 1; sab[0].cElements = 15; sab[1].lLbound = 1; sab[1].cElements = 15; arr.parray = SafeArrayCreate(VT_VARIANT, 2, sab); // 初始化數組內容 for(int i=1; i<=15; i++) { for(int j=1; j<=15; j++) { VARIANT tmp; tmp.vt = VT_BSTR; wsprintfW(szTmp,L"%i,%i",i,j); tmp.bstrVal = SysAllocString(szTmp); // 添加數據到數組中 long indices[] = {i,j}; SafeArrayPutElement(arr.parray, indices, (void *)&tmp); } } // 從Application.ActiveSheet屬性獲得Worksheet對象 IDispatch *pXlSheet; { VARIANT result; VariantInit(&result); AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"ActiveSheet", 0); pXlSheet = result.pdispVal; } // 選擇一個15x15大小的Range IDispatch *pXlRange; { VARIANT parm; parm.vt = VT_BSTR; parm.bstrVal = ::SysAllocString(L"A1:O15"); VARIANT result; VariantInit(&result); AutoWrap(DISPATCH_PROPERTYGET, &result, pXlSheet, L"Range", 1, parm); VariantClear(&parm); pXlRange = result.pdispVal; } ::MessageBox(NULL, "我要填充數據了哈!", "通知", 0x10000); // 用我們的數組填充這個Range AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L"Value", 1, arr); pXlRange->Release(); // 另外再選擇一個Range { VARIANT parm; parm.vt = VT_BSTR; parm.bstrVal = ::SysAllocString(L"A11:O25"); VARIANT result; VariantInit(&result); AutoWrap(DISPATCH_PROPERTYGET, &result, pXlSheet, L"Range", 1, parm); VariantClear(&parm); pXlRange = result.pdispVal; } ::MessageBox(NULL, "我還要填充一次哈!", "通知", 0x10000); // 用我們的數組再次填充這個Range AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L"Value", 1, arr); ::MessageBox(NULL, "好了,我們該保存文件了!", "通知", 0x10000); // 接下來我們該保存文件了,利用Worksheet.SaveAs()方法(我這里忽略了其他所有參數,除了文件名) { VARIANT filename; filename.vt = VT_BSTR; filename.bstrVal = SysAllocString(L"c:\\test.xls"); AutoWrap(DISPATCH_METHOD, NULL, pXlSheet, L"SaveAs", 1, filename); } ::MessageBox(NULL, "哈哈,收工了!", "通知", 0x10000); // 退出,調用Application.Quit()方法 AutoWrap(DISPATCH_METHOD, NULL, pXlApp, L"Quit", 0); // 釋放所有的接口以及變量 pXlRange->Release(); pXlSheet->Release(); pXlBook->Release(); pXlBooks->Release(); pXlApp->Release(); VariantClear(&arr); // 注銷COM庫 CoUninitialize(); return 0; } // AutoWrap 函數的正體(真身,哈哈) // 先聲明:這個函數不是偶寫的哈(別問是誰寫的,偶也不知道) // AutoWrap() - Automation helper function... HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...) { // Begin variable-argument list... va_list marker; va_start(marker, cArgs); if(!pDisp) { MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010); _exit(0); } // Variables used... DISPPARAMS dp = { NULL, NULL, 0, 0 }; DISPID dispidNamed = DISPID_PROPERTYPUT; DISPID dispID; HRESULT hr; char buf[200]; char szName[200]; // Convert down to ANSI WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL); // Get DISPID for name passed... hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID); if(FAILED(hr)) { sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0xlx", szName, hr); MessageBox(NULL, buf, "AutoWrap()", 0x10010); _exit(0); return hr; } // Allocate memory for arguments... VARIANT *pArgs = new VARIANT[cArgs+1]; // Extract arguments... for(int i=0; i<cArgs; i++) { pArgs[i] = va_arg(marker, VARIANT); } // Build DISPPARAMS dp.cArgs = cArgs; dp.rgvarg = pArgs; // Handle special-case for property-puts! if(autoType & DISPATCH_PROPERTYPUT) { dp.cNamedArgs = 1; dp.rgdispidNamedArgs = &dispidNamed; } // Make the call! hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL); if(FAILED(hr)) { sprintf(buf, "IDispatch::Invoke(\"%s\"=lx) failed w/err 0xlx", szName, dispID, hr); MessageBox(NULL, buf, "AutoWrap()", 0x10010); _exit(0); return hr; } // End variable-argument section... va_end(marker); delete [] pArgs; return hr; } 程序在WINDOWS2000+EXCEL2000環境下運行良好,呵呵