VC操作EXCEL

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.****.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               
Excel为发人员提供了强大的外部接口,方便开发人员进行二次开发。最近笔者就采用excel automation技术成功地解决了excel报表的自动生成功能.

首先给大家介绍一下报表创建模块的组成,如下图所示

 

VC操作EXCEL

 

模版文件。由于笔者设计的报表都是周期性的固定报表,做成模版更适合数据的读写操作和图表的生成,所以将全部的报表归纳整理生成了几类模版。

      模版描述文件。主要纪录数据写在那个行那个列,那个图表对应什么类型的数据。它跟随模版文件的变更一起变更,时刻与模版文件保持同步,这样可以不必修改程序就能够更改输出报表的格式。

      数据源描述文件。主要描述对应文件的源数据所在的那种数据库。

 

接下来给大家介绍一下程序操作excel的流程:

一、插入类型库

1、              在一个已有的MFC工程按Ctrl + W 弹出ClassWizard对话框。

2、              Add Class.../From a type Library... Office 目录中,找到你想使用的Excel类型库。若你安装的是Offce2000则选择安装目录下的EXCEL9.OLB文件进行倒入,生成的倒入文件名称叫做excel9.hexcel9.cpp;若你安装的时office2003则选择。选择安装目录下EXCEL.exe进行到入,生成的倒入文件时excel.hexcel.cpp。两者的部分接口有所不同。

3、              在弹出的对话框中选择要添加的类,具体选那些类要根据实际情况而定。当然你也可以全选

说明:类型库会给出Excel的全部接口类,可以根据需要挑选自己需要的类,但是一般情况下的EXCEL文的操作都会涉及以下的几个类:_ApplicationWorkBooks_WorkBookWorkSheets_WorkSheetRange。若涉及图表的操作还会涉及如下的几个类:ChartObjectsChartObject_ChartSeries

 

二、服务的启动和关闭

      服务器的启动

 

  1. _Workbook excelBook;  
  2. Worksheets excelSheets;  
  3. _Application excelApp;  
  4. BOOL CreateApplication()  
  5. {  
  6.     if (!excelApp.CreateDispatch("Excel.Application",NULL))   
  7.     {   
  8.             AfxMessageBox("创建Excel服务失败!");   
  9.             return FALSE;   
  10.     }  
  11.     excelApp.SetVisible(FALSE);   
  12.     excelApp.SetDisplayAlerts(FALSE);         
  13.     return TRUE;  
  14. }  
_Workbook excelBook;Worksheets excelSheets;_Application excelApp;BOOL CreateApplication(){ if (!excelApp.CreateDispatch("Excel.Application",NULL))  {    AfxMessageBox("创建Excel服务失败!");    return FALSE;  } excelApp.SetVisible(FALSE);  excelApp.SetDisplayAlerts(FALSE);        return TRUE;}

说明:

           在此之前要添加CoInitialize(NULL)函数初始化Com接口。

关键词"Excel.Application"是启动ExcelCOM服务的唯一标示。

SetVisible函数表示运行的进程是否可见。

SetDisplayAlerts函数表示是否忽略警告信息,比如提示“文件xxx.xls已经存在是否覆盖?”,SetDisplayAlerts的参数为FALSE则不出现提示信并默认覆盖。

服务的关闭

  1. void DestroryApplication()  
  2. {  
  3.     excelApp.Quit();  
  4.     excelApp.ReleaseDispatch();  
  5. }  
void DestroryApplication(){ excelApp.Quit(); excelApp.ReleaseDispatch();}

三、文件的打开与保存

      文件的打开

 

  1. BOOL Open(char* pFileName)  
  2. {  
  3.     Workbooks excelBooks;  
  4.     excelBooks.AttachDispatch(excelApp.GetWorkbooks());  
  5.     LPDISPATCH lpDisp = excelBooks.Add(SV(pFileName));  
  6.     if(lpDisp == NULL)   
  7.         return FALSE;  
  8.     excelBook.AttachDispatch(lpDisp);  
  9.     excelSheets.AttachDispatch(excelBook.GetWorksheets());  
  10.     return TRUE;  
  11. }  
BOOL Open(char* pFileName){ Workbooks excelBooks; excelBooks.AttachDispatch(excelApp.GetWorkbooks()); LPDISPATCH lpDisp = excelBooks.Add(SV(pFileName)); if(lpDisp == NULL)   return FALSE; excelBook.AttachDispatch(lpDisp); excelSheets.AttachDispatch(excelBook.GetWorksheets()); return TRUE;}

文件的保存

  1. void Save(char* pFile,_Worksheet exceSheet)  
  2. {      
  3.     exceSheet.SaveAs(pFile,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,  
  4.         vtMissing,vtMissing,vtMissing);  
  5. }  
void Save(char* pFile,_Worksheet exceSheet){     exceSheet.SaveAs(pFile,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,  vtMissing,vtMissing,vtMissing);}

说明:

           对于文件的保存2000版和2003版有所区别,2000版比2003版少了一个参数。

四、操作的退出

上面提到得服务的退出不代表文件就关闭了,当服务停止后内存中依然驻留着一个Excel.exe进程必须杀掉该进程才算完整的退出。实现代码如下:

 

  1. void KillProgram(LPCSTR   ExeName)     
  2. {     
  3.     LPCSTR   File;     
  4.     HANDLE   hProcessSnap;     
  5.     PROCESSENTRY32   pe32;     
  6.     MODULEENTRY32   me32;     
  7.     if(!ExeName || !ExeName[0])  
  8.         return;     
  9.     File = strrchr(ExeName, '/');     
  10.         if(File!=0) ExeName = File + 1;     
  11.     hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);     
  12.     if(hProcessSnap == (HANDLE)-1)  
  13.         return;     
  14.     memset(&pe32, 0, sizeof(pe32));     
  15.     pe32.dwSize = sizeof(PROCESSENTRY32);     
  16.     if(Process32First(hProcessSnap, &pe32))     
  17.     {     
  18.             do   
  19.             {     
  20.                 memset(&me32, 0, sizeof(me32));     
  21.                 me32.dwSize = sizeof(me32);     
  22.                 File = strrchr(pe32.szExeFile, '/');     
  23.                     File = File ? File+1 : pe32.szExeFile;     
  24.                 if(strcmpi(File,ExeName) == 0)     
  25.                     EnumWindows((WNDENUMPROC)EnumWinProc, pe32.th32ProcessID);     
  26.             }  
  27.             while(Process32Next(hProcessSnap,&pe32));     
  28.     }   
  29.     CloseHandle(hProcessSnap);     
  30. }   
void KillProgram(LPCSTR   ExeName)   {    LPCSTR   File;    HANDLE   hProcessSnap;    PROCESSENTRY32   pe32;    MODULEENTRY32   me32;    if(!ExeName || !ExeName[0])  return;    File = strrchr(ExeName, '/');     if(File!=0) ExeName = File + 1;    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    if(hProcessSnap == (HANDLE)-1)  return;    memset(&pe32, 0, sizeof(pe32));    pe32.dwSize = sizeof(PROCESSENTRY32);    if(Process32First(hProcessSnap, &pe32))    {      do    {       memset(&me32, 0, sizeof(me32));       me32.dwSize = sizeof(me32);       File = strrchr(pe32.szExeFile, '/');        File = File ? File+1 : pe32.szExeFile;       if(strcmpi(File,ExeName) == 0)        EnumWindows((WNDENUMPROC)EnumWinProc, pe32.th32ProcessID);      }   while(Process32Next(hProcessSnap,&pe32));    }  CloseHandle(hProcessSnap);   }

数据操作
      1.获取sheet
  1. void GetSheet(char* pSheetName,_Worksheet& excelSheet)  
  2. {      
  3.       excelSheet.AttachDispatch(excelSheets.GetItem(_variant_t(pSheetName)));  
  4. }  
  5. _Worksheet dataSheet;  
  6. GetSheet("数据",dataSheet);  
void GetSheet(char* pSheetName,_Worksheet& excelSheet){          excelSheet.AttachDispatch(excelSheets.GetItem(_variant_t(pSheetName)));}_Worksheet dataSheet;GetSheet("数据",dataSheet);
注意:sheet名称必须在文件中存在,否则程序抛出异常。为了适应不同文件的操作,此处的sheet名称可通过配置文件进行动态配置。
2.获取range
  1. void GetRange(_Worksheet excelSheet,Range& excelRange)      
  2. {  
  3.     excelRange.AttachDispatch(excelSheet.GetCells());  
  4. }  
  5. Range dataRange;  
  6. GetRange(dataSheet,dataRange);  
void GetRange(_Worksheet excelSheet,Range& excelRange)    { excelRange.AttachDispatch(excelSheet.GetCells());}Range dataRange;GetRange(dataSheet,dataRange);
3.指定行列的数据获取
  1. #define LV(x)   _variant_t(long(x))  
  2. #define SV(x) _variant_t(x)。  
  3. …  
  4. …  
  5. bool GetData(int row,int col)  
  6. {  
  7.     range.AttachDispatch(dataSheet.GetCells());   
  8.     range.AttachDispatch(range.GetItem(LV(row),LV(col)).pdispVal);  
  9.     COleVariant vResult =range.GetValue2();  
  10.     if(vResult.vt==VT_EMPTY)   
  11.         return false;  
  12.     return true;  
  13. }  
#define LV(x)   _variant_t(long(x))#define SV(x) _variant_t(x)。……bool GetData(int row,int col){ range.AttachDispatch(dataSheet.GetCells());  range.AttachDispatch(range.GetItem(LV(row),LV(col)).pdispVal); COleVariant vResult =range.GetValue2(); if(vResult.vt==VT_EMPTY)   return false; return true;}
4.数据的填写
  1. void SetValue(int row,int col,char* pData)  
  2. {  
  3.     dataRange.SetItem(LV(row),LV(col),SV(pData));   
  4. }  
void SetValue(int row,int col,char* pData){ dataRange.SetItem(LV(row),LV(col),SV(pData)); }
5.数据区域的拷贝
假如有一个表格高度变量是dTableHeight,宽度变量是dTableWidth,将其拷贝若干份,每个表格之间相隔一行实现代码如下:
  1. int row = 1  
  2. Range rgMyRge2, rgMyRge3  
  3. For(int i = 0 ; i < n;i++)  
  4. {  
  5.     rgMyRge2.AttachDispatch( dataRange.GetItem( COleVariant((long)(row)) , COleVariant((long)1)).pdispVal, true);  
  6.     rgMyRge3.AttachDispatch(rgMyRge2.GetResize(COleVariant((long)dTableHeight ),COleVariant((long) dTableWidth)) );  
  7.     rgMyRge3.Copy( dataRange.GetItem(LV((long)( row + dTableHeight)),LV( long(1) ) ) );  
  8.     row = row + dTableHeight + 1;  
  9. }  
int row = 1Range rgMyRge2, rgMyRge3For(int i = 0 ; i < n;i++){ rgMyRge2.AttachDispatch( dataRange.GetItem( COleVariant((long)(row)) , COleVariant((long)1)).pdispVal, true); rgMyRge3.AttachDispatch(rgMyRge2.GetResize(COleVariant((long)dTableHeight ),COleVariant((long) dTableWidth)) ); rgMyRge3.Copy( dataRange.GetItem(LV((long)( row + dTableHeight)),LV( long(1) ) ) ); row = row + dTableHeight + 1;}
图表操作
Excel提供了功能强大的图表处理功能,我们可以轻而易举的绘制各类统计报表。如下图所示我们将对局有固定模版的图表进行处理。
 VC操作EXCEL
 
1.              获得图表
  1. _Worksheet chartSheet;  
  2. GetSheet("曲线图",chartSheet);  
  3.   
  4. void GetChart(_Worksheet excelSheet,int dChatNo,_Chart& excelChart)  
  5. {  
  6.     ChartObjects objCharts;  
  7.     ChartObject objChart;  
  8.     COleVariant vOpt(DISP_E_PARAMNOTFOUND, VT_ERROR);  
  9.   
  10.     objCharts = excelSheet.ChartObjects(vOpt);  
  11.     objChart = objCharts.Item(COleVariant((short)dChatNo));   
  12.     excelChart.AttachDispatch( objChart.GetChart(),true);  
  13. }  
  14. _Chart resultchart;  
  15. GetChart(chartSheet,ChartNo,resultchart);    
_Worksheet chartSheet;GetSheet("曲线图",chartSheet);void GetChart(_Worksheet excelSheet,int dChatNo,_Chart& excelChart){ ChartObjects objCharts; ChartObject objChart; COleVariant vOpt(DISP_E_PARAMNOTFOUND, VT_ERROR); objCharts = excelSheet.ChartObjects(vOpt); objChart = objCharts.Item(COleVariant((short)dChatNo));  excelChart.AttachDispatch( objChart.GetChart(),true);}_Chart resultchart;GetChart(chartSheet,ChartNo,resultchart); 
注意:变量ChartNo表示图表的排序值,即属于一个sheet中的第几个图表,该排序值是根据用户创建图表的顺序自动生成的。
2.              设定曲线
要实现曲线的,就必须准确的描述曲线。曲线描述信息是一个序列化的字符串,它包含四个部分,每个部分之间依靠逗号分隔:
1.              图表的title.
2.              图表的x周描述信息
3.              显示曲线所基于的数据区域描述
4.              单一图表内的曲线排序值(曲线id
其中某些项目可以不设,系统将取默认值,最终形成的序列化格式如下:
=SERIES("示例",数据!$A$4:$A$34,数据!$C$4:$C$34,1),也可以省略指定x坐标
=SERIES("示例", ,数据!$C$4:$C$34,1)
  1. void SetChartSeriesSource( _Chart excelChart , short series_id , char *pParamLine)  
  2. {  
  3.     Series oSeries;  
  4.     oSeries = excelChart.SeriesCollection(COleVariant((short)series_id));  
  5.     oSeries.SetFormula(pParamLine);  
  6. }  
  7.   
  8. char szParamLine[1024];         
  9. sprintf(rgnline,"=SERIES(/"%s/",,%s!$%s$%d:$%s$%d,%d)""数据",  
  10.         pDataName,pCol,row,pCol,dIndexRow[i] , 1);  
  11. SetChartSeriesSource(excelChart , 1 , szParamLine);  
void SetChartSeriesSource( _Chart excelChart , short series_id , char *pParamLine){ Series oSeries; oSeries = excelChart.SeriesCollection(COleVariant((short)series_id)); oSeries.SetFormula(pParamLine);}char szParamLine[1024];       sprintf(rgnline,"=SERIES(/"%s/",,%s!$%s$%d:$%s$%d,%d)", "数据",  pDataName,pCol,row,pCol,dIndexRow[i] , 1);SetChartSeriesSource(excelChart , 1 , szParamLine);
注意:设定的数据内不得出现没有值得现象。比如基于数据区域a1:a100形成一条曲线,若a30的位置无数据(指的是区域空而非零)则程序抛出异常。
           

给我老师的人工智能教程打call!http://blog.****.net/jiangjunshow

VC操作EXCEL