最近公司有個項目要用到Excel的操作,於是自己就對VC中關於Excel的操作進行整理了下。而這里我是直接做成DLL方便他人調用的。
-
創建一個MFC Dll項目。

- 選擇MFC擴展DLL。

- 在“類視圖”---“添加類”---在MFC中選擇“TypeLib中的MFC類”,按如圖所示選擇,並添加對應的類
PS:由於我電腦上安裝的是office2010,所以對應的Excel版本號是14.0,各位可以根據自己的情況選擇。 - 刪除這六個類的頭文件中的第一句,如圖,並將頭文件添加到“stdafx.h"中


- 修改字符集,vs2010默認生成的是使用unicode字符集,這里我們把它修改為”使用多字節字符集“,這樣能避免很多不必要的麻煩。

- 接下來進行編譯,正常情況下會出現如下錯誤:
這時候定位到警告處,在函數名之前加上下划線就OK了。如圖:
這樣編譯就沒什么問題了。 -
接下就是直接添加導出函數來,這里我偷個懶直接到dllmain.cpp這個文件里面加了,大家可以根據自己的習慣來。
1 extern "C" _declspec(dllexport) void ReadExcel(const CString& strPath,const CString& strFlag, const vector<CString>& vTarget,vector<CString>& vOutTarget) 2 { 3 CApplication _app; 4 CWorkbook _book; 5 CWorkbooks _books; 6 7 CWorksheet _sheet; 8 CWorksheets _sheets; 9 CRange _rang; 10 11 if (!_app.CreateDispatch(_T("Excel.Application"))) 12 { 13 MessageBox(NULL,_T("Error!Creat Excel Application Server Faile!"),_T("Creat Excel Application Server"),MB_ICONERROR); 14 exit(1); 15 } 16 _books = _app.get_Workbooks(); 17 _book = _books.Add(_variant_t(strPath)); 18 19 _sheets = _book.get_Worksheets(); 20 _sheet = _sheets.get_Item(_variant_t("電力電纜井")); 21 22 //獲取行列數 23 CRange _usedRang; 24 _usedRang.AttachDispatch(_sheet.get_UsedRange(),TRUE); 25 _rang.AttachDispatch(_usedRang.get_Columns(),TRUE); 26 long nColumn = _rang.get_Count();//列 27 _rang.ReleaseDispatch(); 28 _rang.AttachDispatch(_usedRang.get_Rows(),TRUE); 29 long nRow = _rang.get_Count();//行 30 _rang.ReleaseDispatch(); 31 _usedRang.ReleaseDispatch(); 32 //遍歷 33 _rang.AttachDispatch(_sheet.get_Cells(),TRUE);//獲取所有單元格 34 CString str; 35 long nCurRow = 0; 36 long nCurColumn = 0; 37 //獲取關鍵字所在的行列號 38 for (long i = 1; i <= nRow; ++i) 39 { 40 for (long j = 1; j <= nColumn; ++j) 41 { 42 CRange _currentRang; 43 _currentRang.AttachDispatch(_rang.get_Item(COleVariant((long)i),COleVariant((long)j)).pdispVal,TRUE); 44 COleVariant vResult = _currentRang.get_Value2(); 45 46 if (VT_BSTR == vResult.vt)//字符串 47 { 48 str = vResult.bstrVal; 49 } 50 else if (VT_INT==vResult.vt) 51 { 52 str.Format(_T("%d"),vResult.pintVal); 53 } 54 else if (VT_R8==vResult.vt)//8字節數字 55 { 56 str.Format(_T("%f"),vResult.dblVal); 57 } 58 else if (VT_DATE==vResult.vt)//時間格式 59 { 60 SYSTEMTIME st; 61 VariantTimeToSystemTime(vResult.date,&st); 62 } 63 else if (VT_EMPTY==vResult.vt)//空單元格 64 { 65 str = "(NULL)"; 66 } 67 _currentRang.ReleaseDispatch(); 68 if (strFlag==str)//直接使用goto語句跳出雙重循環 69 { 70 nCurRow = i; 71 nCurColumn = j; 72 goto LOOPOUT; 73 } 74 } 75 } 76 77 LOOPOUT: 78 vector<long> vNTarget;//目標列所在的列號 79 for (long i = 1;i <= nColumn;++i) 80 { 81 CRange _currentRang; 82 _currentRang.AttachDispatch(_rang.get_Item(COleVariant((long)1),COleVariant((long)i)).pdispVal,TRUE); 83 COleVariant vResult = _currentRang.get_Value2(); 84 85 if (VT_BSTR == vResult.vt)//字符串 86 { 87 str = vResult.bstrVal; 88 } 89 else if (VT_INT==vResult.vt) 90 { 91 str.Format(_T("%d"),vResult.pintVal); 92 } 93 else if (VT_R8==vResult.vt)//8字節數字 94 { 95 str.Format(_T("%f"),vResult.dblVal); 96 } 97 else if (VT_DATE==vResult.vt)//時間格式 98 { 99 SYSTEMTIME st; 100 VariantTimeToSystemTime(vResult.date,&st); 101 } 102 else if (VT_EMPTY==vResult.vt)//空單元格 103 { 104 str = "(NULL)"; 105 } 106 _currentRang.ReleaseDispatch(); 107 //對取出來的str和目標str進行配對 108 for (vector<CString>::size_type j = 0;j != vTarget.size(); ++j) 109 { 110 if (vTarget.at(j)==str) 111 { 112 vNTarget.push_back(i); 113 break; 114 } 115 } 116 } 117 //接下來根據獲取的行號nCurRow和列號vNTarget來讀取指定內容 118 for (vector<CString>::size_type i = 0; i != vTarget.size(); ++i) 119 { 120 CRange _currentRang; 121 _currentRang.AttachDispatch(_rang.get_Item(COleVariant((long)nCurRow),COleVariant((long)vNTarget.at(i))).pdispVal,TRUE); 122 COleVariant vResult = _currentRang.get_Value2(); 123 124 if (VT_BSTR == vResult.vt)//字符串 125 { 126 str = vResult.bstrVal; 127 } 128 else if (VT_INT==vResult.vt) 129 { 130 str.Format(_T("%d"),vResult.pintVal); 131 } 132 else if (VT_R8==vResult.vt)//8字節數字 133 { 134 str.Format(_T("%f"),vResult.dblVal); 135 } 136 else if (VT_DATE==vResult.vt)//時間格式 137 { 138 SYSTEMTIME st; 139 VariantTimeToSystemTime(vResult.date,&st); 140 } 141 else if (VT_EMPTY==vResult.vt)//空單元格 142 { 143 str = "(NULL)"; 144 } 145 _currentRang.ReleaseDispatch(); 146 vOutTarget.push_back(str); 147 } 148 149 _book.ReleaseDispatch(); 150 _books.ReleaseDispatch(); 151 _app.Quit();
152 _app.ReleaseDispatch();153 }
上述函數就是打開路徑strPath的Excel文件,並通過標志符strFlag找到要讀取的行,通過vTarget找到要讀取的列,將讀取的內容輸出到vOutTarget中。這里我是默認表格的第一行就是存放的表頭,大家可以根據自己的實際情況進行調整。 - 接下來再添加一個關於更新(即寫某個單元格的函數)
1 extern "C" _declspec(dllexport) void UpdateExcel(const CString& strPath,const CString& strFlag,const vector<CString>& vTarget,const vector<CString>& vInTarget) 2 { 3 CApplication _app; 4 CWorkbook _book; 5 CWorkbooks _books; 6 7 CWorksheet _sheet; 8 CWorksheets _sheets; 9 CRange _rang; 10 11 if (!_app.CreateDispatch(_T("Excel.Application"))) 12 { 13 MessageBox(NULL,_T("Error!Creat Excel Application Server Faile!"),_T("Creat Excel Application Server"),MB_ICONERROR); 14 exit(1); 15 } 16 _books = _app.get_Workbooks(); 17 _book = _books.Add(_variant_t(strPath)); 18 19 _sheets = _book.get_Worksheets(); 20 _sheet = _sheets.get_Item(_variant_t("電力電纜井")); 21 22 //獲取行列數 23 CRange _usedRang; 24 _usedRang.AttachDispatch(_sheet.get_UsedRange(),TRUE); 25 _rang.AttachDispatch(_usedRang.get_Columns(),TRUE); 26 long nColumn = _rang.get_Count();//列 27 _rang.ReleaseDispatch(); 28 _rang.AttachDispatch(_usedRang.get_Rows(),TRUE); 29 long nRow = _rang.get_Count();//行 30 _rang.ReleaseDispatch(); 31 _usedRang.ReleaseDispatch(); 32 //遍歷 33 _rang.AttachDispatch(_sheet.get_Cells(),TRUE);//獲取所有單元格 34 CString str; 35 long nCurRow = 0; 36 long nCurColumn = 0; 37 //獲取關鍵字所在的行列號 38 for (long i = 1; i <= nRow; ++i) 39 { 40 for (long j = 1; j <= nColumn; ++j) 41 { 42 CRange _currentRang; 43 _currentRang.AttachDispatch(_rang.get_Item(COleVariant((long)i),COleVariant((long)j)).pdispVal,TRUE); 44 COleVariant vResult = _currentRang.get_Value2(); 45 46 if (VT_BSTR == vResult.vt)//字符串 47 { 48 str = vResult.bstrVal; 49 } 50 else if (VT_INT==vResult.vt) 51 { 52 str.Format(_T("%d"),vResult.pintVal); 53 } 54 else if (VT_R8==vResult.vt)//8字節數字 55 { 56 str.Format(_T("%f"),vResult.dblVal); 57 } 58 else if (VT_DATE==vResult.vt)//時間格式 59 { 60 SYSTEMTIME st; 61 VariantTimeToSystemTime(vResult.date,&st); 62 } 63 else if (VT_EMPTY==vResult.vt)//空單元格 64 { 65 str = "(NULL)"; 66 } 67 _currentRang.ReleaseDispatch(); 68 if (strFlag==str)//直接使用goto語句跳出雙重循環 69 { 70 nCurRow = i; 71 nCurColumn = j; 72 goto LOOPOUT; 73 } 74 } 75 } 76 77 LOOPOUT: 78 vector<long> vNTarget;//目標列所在的列號 79 for (long i = 1;i <= nColumn;++i) 80 { 81 CRange _currentRang; 82 _currentRang.AttachDispatch(_rang.get_Item(COleVariant((long)1),COleVariant((long)i)).pdispVal,TRUE); 83 COleVariant vResult = _currentRang.get_Value2(); 84 85 if (VT_BSTR == vResult.vt)//字符串 86 { 87 str = vResult.bstrVal; 88 } 89 else if (VT_INT==vResult.vt) 90 { 91 str.Format(_T("%d"),vResult.pintVal); 92 } 93 else if (VT_R8==vResult.vt)//8字節數字 94 { 95 str.Format(_T("%f"),vResult.dblVal); 96 } 97 else if (VT_DATE==vResult.vt)//時間格式 98 { 99 SYSTEMTIME st; 100 VariantTimeToSystemTime(vResult.date,&st); 101 } 102 else if (VT_EMPTY==vResult.vt)//空單元格 103 { 104 str = "(NULL)"; 105 } 106 _currentRang.ReleaseDispatch(); 107 //對取出來的str和目標str進行配對 108 for (vector<CString>::size_type j = 0;j != vTarget.size(); ++j) 109 { 110 if (vTarget.at(j)==str) 111 { 112 vNTarget.push_back(i); 113 break; 114 } 115 } 116 } 117 //更新指定的單元格 118 for (vector<CString>::size_type i = 0; i != vInTarget.size(); ++i) 119 { 120 _rang.put_Item(COleVariant(nCurRow),COleVariant(vNTarget.at(i)),COleVariant(vInTarget.at(i))); 121 }
1 _book.SaveCopyAs(COleVariant(strPath)); 2 _book.put_Saved(TRUE); 3 _app.put_Visible(FALSE); 4 _book.ReleaseDispatch(); 5 _books.ReleaseDispatch(); 6 7 _app.Quit() 8 _app.ReleaseDispatch();
126 }
參數的設置和上一個函數差不多。 - 接下來編譯的時候,正常情況下會有一點小問題。這個問題如果你是基於對話框的MFC程序貌似是不會出現的,只有創建DLL或者ActiveX控件才會出現。
這個問題也是煩惱了我好久,解決方法是先在文件頭導入一個lib
在編譯,運氣好的話就沒什么問題了。運氣不好還會出下如下問題。
這個時候我們修改下項目屬性
把我標出來的選項改為否應該就沒什么問題了。 - 最后編譯完成通過。這樣dll和lib就都生成好了,大家可以盡情的使用了。
- 差不多就這樣了,至於其他的問題,比如如果直接創建的是基於對話框的MFc程序的話就更簡單了。跟着步驟走就完事了。最后我是一個菜鳥,如果有什么問題也歡迎大家指教。
