啰嗦一下:本人所在公司從事碟式斯特林太陽能發電設備的研發與銷售。單台設備圖如下:

工作原理如下:整個設備大致可分為五個部分,
1、服務器,負責氣象、發電等數據存取,電、網連接等處理;
2、氣象站,通過光感應器實時獲取氣象數據,傳送至服務器
3、碟型鏡面,反射陽光熱量,均勻聚焦到350mm直徑的圓形范圍內——發動機熱頭大小;
4、斯特林發動機,吸收鏡面反射熱量,通過內部氣體膨脹、收縮帶動發動機發電;
5、通信線,連接系統各個部分,使其運行正常。
對於這樣一台設備,要使其高效發電,出氣象條件要好之外,對每塊鏡片的最終安裝位置要求極高。因為所有的支撐架子、鏡片的形狀、曲率等參數不可能做到一模一樣,所以每台設備在鏡片初步安裝之后都必須進行精細的調整。具體技術問題這里暫不討論。
而對於一個電站,由幾十或成百上千台碟子組成,除了鏡片調試以外,還有一個重要的問題需要解決:所有碟子的布局。因為,若碟子之間的距離過小,會有遮擋,造成部分鏡面沒有陽光照射,發動機吸收熱量減少,影響發電效率,嚴重的話會使其停止工作;而若距離過大,解決遮擋的同時,會造成整個電站面積過大,意味着通信線與土地的代價增加。因此,在這之間尋求一種折中的解決方案至關重要,做到盡量高效,低成本。

針對斯特林碟式太陽能發電站的碟子位置布局問題,公司要求做一個軟件,主要功能是分析歷史電站的發電、氣象、電價、布線等數據,並結合擬定的碟子布局模型,比較實際與模擬結果,選擇最佳方案。
回歸正題:文件存儲在Excel文件中,因此第一步是能夠在程序中方便地讀取表格數據,這里用的是VC6.0 MFC。
1、電腦上安裝VC6.0,Excel2007,其他Office版本亦可(WPS盡量避免);
2、建立一個MFC基於對話框的應用程序工程,名稱自己定,這里取名為"Solar",添加一個按鈕、一個列表控件,分別取名為"Model2","List1",並為后者添加成員變量"m_ExcelData"。
3、在程序入口處CXXXApp:: InitInstance()函數AfxEnableControlContainer();語句之后加入下面幾行:
1 if (CoInitialize(NULL) != 0) 2 { 3 AfxMessageBox(“初始化COM支持庫失敗!”); 4 exit(1); 5 }
假如這個條件不通過就不能運行起程序。在程序的出口處CSolarApp:: ExitInstance()函數return語句之前加入下面這句話:
1 CoUninitialize();
來釋放COM支持庫,這樣對COM庫的支持就已完成。
4、下面要從Office的安裝目錄中找到對VC操作excel文件的動態庫,在某些版本下這個文件是Excel8.olb或Excel9.olb,在我的版本中是excel.exe這個exe也是動態庫的形式,是微軟公司主要的文件結果之一。選擇VC的View(查看)菜單里面的類向導ClassWizad命令,會彈出一個對話框;然后點擊Add Class…按鈕選擇From a type library,會彈出一個打開對話框,從這里打開Office安裝目錄下D:\Program Files\Microsoft Office\Office12\EXCEL.EXE文件,從里面選擇幾個要用到的類:_Application, Workbooks, _Wrokbook, Worksheets, _WorkSheet, Range,點擊OK按鈕。會在程序中生成一個excel.h和excel.cpp文件,這些文件中包含了剛才我們選擇的幾個類的代碼。下面介紹一下這幾個類:
在VC操縱excel的exe動態庫里面有好多個對象模型,就是剛才在創建過程中看到的那個列表,但是經常用到的有這么幾個:_Application, Workbooks, _Wrokbook, Worksheets, _WorkSheet, Range,Charts和_Chart,最后面的兩個是用來操作圖表的,這里沒有用到所以這里也就不記錄了。
1>_Application:這里的Application就是Excel本身,眾所周知,一個Excel可以包含多個工作簿,每個工作簿又可以包含多個工作表,而每個工作表又可以包含多個區域或者圖表,所以這里他們是樹型的結構關系,而application最基本的一個功能就是找到它的子項工作簿。果然,我們在引入我們程序的Application類中看到了這樣的成員函數:GetWorkbooks()。
2> Workbooks:這個對象是一個容器對象,它里面存放着所有打開的工作簿。因此,我們可以猜測它一定有添加,查找,打開/關閉工作簿的功能。(本程序中使用excel的一個xlt模板來生成一個xls文件就是使用了這個容器對象的添加功能。)
3> _Workbook:這是一個工作簿,也就相當於一個xls文件。Excel可以同時打開多個工作簿,所以工作簿之間必定能夠互相切換,每個工作簿可以關聯工作表容器並獲得工作表的索引。
4> Worksheets:也是一個容器對象,和Workbooks類似。
5> _Worksheet:這個就是我們看到的工作表,比如Sheet1,sheet2等等。
6> Rang:就是我們看到的能選中的方框的大小。而我們所要作的操作基本上是以區域為單位進行的。
5、准備工作做好之后,接下來開始實現按鈕功能:按下之后讀取Excel表格數據,並顯示在列表控件中。這里只是進行簡單地顯示,沒有給出具體的處理過程及結果圖像。目標Excel文件數據如下,行、列分別為30、9(包括序號)。

在頭文件"afxstd.h"中添加以下幾個頭文件(有些電腦環境可能不必全部加),注意別重復添加某些頭文件。
1 #include <comdef.h> 2 #include <comutil.h> 3 #include <OAIDL.H> 4 #include <afxdisp.h> 5 #include "excel.h"
若編譯報重定義錯誤,解決方法是在"excel.h"頭文件中的頭部添加:
1 #if !defined _HEAD_FILE_EXCEL9_ 2 #define _HEAD_FILE_EXCEL9_
在其尾部添加:
#endif
在對話框初始化函數BOOL CSolarDlg::OnInitDialog()中添加列表控件風格設置語句:
1 m_Exceldata.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);
OK,在函數BOOL CSolarDlg::OnModel2()中添加數據讀取及顯示代碼:
1 _Application m_oExcelApp; 2 Worksheets m_oWorkSheets; 3 _Worksheet m_oWorkSheet; 4 Workbooks m_oWorkBooks; 5 _Workbook m_oWorkBook; 6 Range m_oCurrRange; 7 if (!m_oExcelApp.CreateDispatch( _T( "Excel.Application" ), NULL ) ) 8 { 9 ::MessageBox( NULL, _T( "創建Excel服務失敗!" ), _T( "錯誤提示!" ), MB_OK | MB_ICONERROR); 10 exit(1); 11 } 12 13 //設置為顯示 14 m_oExcelApp.SetVisible(FALSE); 15 m_oWorkBooks.AttachDispatch( m_oExcelApp.GetWorkbooks(), TRUE ); //沒有這條語句,下面打開文件返回失敗。 16 17 LPDISPATCH lpDisp = NULL; 18 COleVariant covTrue((short)TRUE); 19 COleVariant covFalse((short)FALSE); 20 COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); 21 Range oCurCell; 22 23 CString FilePathName; 24 CFileDialog dlg(TRUE);///TRUE為OPEN對話框,FALSE為SAVE AS對話框 25 if(dlg.DoModal() == IDOK) 26 { 27 FilePathName = dlg.GetPathName(); 28 /* 29 (1)GetPathName();取文件名全稱,包括完整路徑。取回C:\\WINDOWS\\TEST.EXE 30 (2)GetFileTitle();取回TEST 31 (3)GetFileName();取文件全名:TEST.EXE 32 (4)GetFileExt();取擴展名EXE 33 */ 34 } 35 else 36 { 37 AfxMessageBox("Open file opetation has been canceled."); 38 return; 39 } 40 41 // 打開文件 42 lpDisp = m_oWorkBooks.Open( FilePathName, 43 _variant_t(vtMissing), 44 _variant_t(vtMissing), 45 _variant_t(vtMissing), 46 _variant_t(vtMissing), 47 _variant_t(vtMissing), 48 _variant_t(vtMissing), 49 _variant_t(vtMissing), 50 _variant_t(vtMissing), 51 _variant_t(vtMissing), 52 _variant_t(vtMissing), 53 _variant_t(vtMissing), 54 _variant_t(vtMissing), 55 _variant_t(vtMissing), 56 _variant_t(vtMissing) ); 57 // 獲得活動的WorkBook( 工作簿 ) 58 m_oWorkBook.AttachDispatch( lpDisp, TRUE ); 59 // 獲得活動的WorkSheet( 工作表 ) 60 m_oWorkSheet.AttachDispatch( m_oWorkBook.GetActiveSheet(), TRUE ); 61 // 獲得使用的區域Range( 區域 ) 62 m_oCurrRange.AttachDispatch( m_oWorkSheet.GetUsedRange(), TRUE ); 63 64 // 獲得使用的行數 65 long lgUsedRowNum = 0; 66 m_oCurrRange.AttachDispatch( m_oCurrRange.GetRows(), TRUE ); 67 lgUsedRowNum = m_oCurrRange.GetCount(); 68 // 獲得使用的列數 69 long lgUsedColumnNum = 0; 70 m_oCurrRange.AttachDispatch( m_oCurrRange.GetColumns(), TRUE ); 71 lgUsedColumnNum = m_oCurrRange.GetCount(); 72 // 讀取Sheet的名稱 73 CString strSheetName = m_oWorkSheet.GetName(); 74 75 //得到全部Cells,此時,CurrRange是cells的集合 76 m_oCurrRange.AttachDispatch( m_oWorkSheet.GetCells(), TRUE ); 77 78 // 更新列表控件數據 79 for ( int j = 0; j < lgUsedColumnNum; ++j) 80 { 81 if(j == 9) 82 { 83 m_Exceldata.InsertColumn(j,"Index",LVCFMT_CENTER); 84 m_Exceldata.SetColumnWidth(j,40); 85 continue; 86 } 87 oCurCell.AttachDispatch( m_oCurrRange.GetItem( COleVariant( (long)(0 + 1)), COleVariant( (long)j ) ).pdispVal, TRUE ); 88 VARIANT varItemName = oCurCell.GetText(); 89 strItemName = varItemName.bstrVal; 90 m_Exceldata.InsertColumn(j,strItemName,LVCFMT_CENTER); 91 m_Exceldata.SetColumnWidth(j,80); 92 } 93 94 for ( int i = 1; i < lgUsedRowNum; ++i) //標題欄0+1行已經取出,從1+1開始獲取數據到29+1 95 { 96 strItemName.Format("%d",i); 97 m_Exceldata.InsertItem(i-1,strItemName); 98 m_Exceldata.SetItemText(i-1,0,strItemName); 99 for ( int j = 1; j < lgUsedColumnNum; ++j) //1-8 100 { 101 oCurCell.AttachDispatch( m_oCurrRange.GetItem( COleVariant( (long)(i + 1)), COleVariant( (long)j ) ).pdispVal, TRUE ); 102 VARIANT varItemName = oCurCell.GetText(); 103 strItemName = varItemName.bstrVal; 104 105 // 判斷是否是合並的單元格 106 VARIANT varMerge = oCurCell.GetMergeCells(); 107 if ( varMerge.boolVal == -1 ) 108 { 109 AfxMessageBox( _T( "是合並的單元格!" ) ); 110 } 111 else if ( varMerge.boolVal == 0 ) 112 { 113 AfxMessageBox( _T( "不是合並的單元格!" ) ); 114 } 115 116 m_Exceldata.SetItemText(i-1,j,strItemName); 117 } 118 } 119 120 // 關閉 121 m_oWorkBook.Close( covOptional, COleVariant( FilePathName ), covOptional ); 122 m_oWorkBooks.Close(); 123 // 釋放 124 m_oCurrRange.ReleaseDispatch(); 125 m_oWorkSheet.ReleaseDispatch(); 126 m_oWorkSheets.ReleaseDispatch(); 127 m_oWorkBook.ReleaseDispatch(); 128 m_oWorkBooks.ReleaseDispatch(); 129 m_oExcelApp.ReleaseDispatch(); 130 m_oExcelApp.Quit(); // 這條語句是推出Excel程序,任務管理器中的EXCEL進程會自動結束
代碼中字符串變量strItemName是全局變量,在文件開頭聲明:
1 CString strItemName = " ";
須注意的是:
1> 代碼中79-92行是為列表添加屬性列及名稱,第一個屬性序號("Index")是人為命名並添加,不是必須的;從第二個屬性列開始為表格屬性;
2> 代碼中23-39行是文件打開過程,選擇Excel文件后,下面代碼會根據完整文件名進行數據讀取;
3> 在VC數組及列表控件中,下標從0開始,而讀取Excel表格數據時,索引從1開始。
至此,簡單的編程過程已完成,重新生成解決方案,編譯、運行,結果如下:

