https://blog.csdn.net/xiaoxiangyuan123456/article/details/70941588
1.引言
通过VC实现对Excel表格的操作的方法有多种,如:通过ODBC数据库实现,通过解析Excel表格文件,通过OLE/COM的实现。本文主要研究通过OLE/COM实现对Excel表格的操作。
本文源码的应用环境说明:
Windows7 X64
Microsoft Visual Studio 2010
Microsoft Office Excel 2010
2.准备工作
1.导入并封装Excel的接口
Excel作为OLE/COM库插件,定义好了各类交互的接口,这些接口是跨语言的接口。VC可以通过导入这些接口,并通过接口来对Excel的操作。由于本文只关心对Excel表格中的数据的读取,主要关注几个_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range等几个接口。Excel的各类接口的属性、方法可以通过MSDN的Office Development进行查询。VS2010导入OLE/COM组件的接口的步骤为:Project->ClassWizard->Add Class->MFC Class From TypeLib,先选择要导入的组件所在的路径,即Excel.exe所在的路径,然后再选择要导入的Excel类型库中的接口。具体操作如图所示:
在完成接口导入后,VS2010将自动为导入的接口创建相应的实现类,用于对接口属性和方法的实现。由于标准的C++没有属性访问器,只能添加一个两个存取函数来实现对属性的访问,通过在属性名称前加上get_和put_前缀分别实现对属性的读写操作。即,由VC自动完成C++类对接口的封装。
本文所导入的接口对应的类和头文件的说明如下所示:
Application
CApplicaton
Application.h
Excel应用程序。
Workbooks
CWorkbooks
Workbooks.h
工作簿的容器,里面包括了Excel应用程序打开的所有工作簿。
_Workbook
CWorkbook
Workbook.h
单个工作簿。
Worksheets
CWorksheets
Worksheets.h
单个工作簿中的Sheet表格的容器,包括该工作簿中的所有Sheet。
_Worksheet
CWorksheet
Worksheet.h
单个Sheet表格。
Range
CRange
Range.h
一定数量的单元格,可对单元格进行单个或多个单元格进行操作。
备注:1.导入接口类后,将CApplication、CWorkbooks、CWorkbook、CWorksheet、CWorksheets、CRange类头文件中的#import语句屏蔽。如下所示:
2.在stafx.h文件中加入以下头文件包含语句:
#include <afxdisp.h> // MFCAutomation classes
#include "CApplication.h"
#include "CRange.h"
#include "CWorkbook.h"
#include "CWorkbooks.h"
#include "CWorksheet.h"
#include "CWorksheets.h"
2. 添加OLE/COM支持
应用程序必须添加对OLE/COM的支持,才能导入OLE/COM组件。本文使用的是MFC对话框程序,在创建工程的向导中选中Automation选项即可为程序自动添加相应的头文件和OLE库初始化代码。在App类中的InitInstance()加入如下语句:
if (CoInitialize(NULL)!= 0)
{
AfxMessageBox(_T("初始化失败"));
exit(1);
}
用ClassWizard给App类添加虚函数ExitInstance(),在ExitInstance()中加入如下语句,进行com库资源释放.
CoUninitialize(); //释放com资源
现在运行程序会出现一个错误,双击错误提示,定位在错误行,
VARIANT DialogBox()
{
VARIANT result;
InvokeHelper(0xf5, DISPATCH_METHOD, VT_VARIANT, (void*)&result, NULL);
return result;
}
将该函数名“DialogBox()”前面加“_”下划线,即“_DialogBox()”,这样就可以编译成功了。
3.操作Excel
1.原理
Excel操作原理图
其中Application是Excel应用程序,每个Application都有一个工作薄容器Workbooks,工作薄容器中存放着所有的工作簿Workbook,我们平常打开的Excel都是一个工作簿;而每个工作簿中都有一个工作表容器Worksheets,工作表容器中存放着所有的工作表Worksheet;每个工作表中都许多有单元格Range。往Excel单元格中写入内容的过程就是这样一层层往下遍历。
2.操作Excel
操作Excel的主要步骤如下:
(1)创建一个Excel应用程序。
(2)得到Workbook的容器。
(3)打开一个Workbook或者创建一个Workbook。
(4)得到Workbook中的Worksheet的容器。
(5)打开一个Worksheet或者创建一个WorkSheet。
(6)通过Range对WorkSheet中的单元格进行读写操作。
(7)保存Excel。
(8)释放资源。
具体代码如下所示:
CApplication ExcelApp;
CWorkbooks wbsMyBooks;
CWorkbook wbMyBook;
CWorksheets wsMySheets;
CWorksheet wsMySheet;
CRange rgMyRge;
CChart chart;
CChartObjects chartObjects; //图表容器对象
CChartObject chartObject; //图表对象
COleVariant
covTrue((short)TRUE),
covFalse((short)FALSE),
covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
LPDISPATCH lpDisp = NULL;
//创建一个EXCEL应用程序实例
if (!ExcelApp.CreateDispatch(_T("Excel.Application"), NULL))
{
AfxMessageBox(_T("创建Excel程序失败"));
exit(1);
}
ExcelApp.put_Visible(TRUE); //使程序可见
ExcelApp.put_UserControl(FALSE);
//将工作簿容器对象与应用程序工作簿关联
wbsMyBooks.AttachDispatch(ExcelApp.get_Workbooks());
//打开一个工作簿,如果不存在,则新增一个工作簿
CString strFile = _T("C:\\Users\\Administrator\\Desktop\\123.xlsx");
try
{
//打开一个工作簿
lpDisp = wbsMyBooks.Open(strFile, vtMissing, vtMissing, vtMissing, vtMissing,
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing,
vtMissing, vtMissing, vtMissing, vtMissing, vtMissing);
//将工作簿对象和打开的工作簿关联
wbMyBook.AttachDispatch(lpDisp);
}
catch (...)
{
//增加一个新的工作簿
lpDisp = wbsMyBooks.Add(vtMissing);
wbMyBook.AttachDispatch(lpDisp);
}
//获得工作表容器对象实例
wsMySheets.AttachDispatch(wbMyBook.get_Sheets());
//打开一个工作表,如果不存在则新增一个
CString strSheetName = _T("NewSheet");
try
{
//打开一个已有的工作表(wooksheet)
lpDisp = wsMySheets.get_Item(_variant_t(strSheetName));
wsMySheet.AttachDispatch(lpDisp);
}
catch(...)
{
lpDisp = wsMySheets.Add(vtMissing, vtMissing, _variant_t((long)1), vtMissing);
wsMySheet.AttachDispatch(lpDisp);
wsMySheet.put_Name(strSheetName); //创建工作表名
}
system("pause");
//设置单元格的范围为全部区域
lpDisp = wsMySheet.get_Cells();
rgMyRge.AttachDispatch(lpDisp);
//插入标题
rgMyRge.put_Item(_variant_t((LONG)1),_variant_t((LONG)1),_variant_t("X"));
rgMyRge.put_Item(_variant_t((LONG)1),_variant_t((LONG)2),_variant_t("Y1"));
rgMyRge.put_Item(_variant_t((LONG)1),_variant_t((LONG)3),_variant_t("Y2"));
// 将数据填入Excel表格
for (int i = 0; i < 8; i++)
{
rgMyRge.put_Item(_variant_t((LONG)(2+i)),_variant_t((LONG)1),_variant_t((long)(i+1)));
rgMyRge.put_Item(_variant_t((LONG)(2+i)),_variant_t((LONG)2),_variant_t((long)((i+1)*(i+1))));
rgMyRge.put_Item(_variant_t((LONG)(2+i)),_variant_t((LONG)3),_variant_t((long)((i+1)*2)));
}
//图表操作//
//图表chart
double left = 200;
double top = 160;
double width = 450;
double height = 260;
//关联图表容器对象
lpDisp = wsMySheet.ChartObjects(covOptional);
ASSERT(lpDisp);
chartObjects.AttachDispatch(lpDisp);
//新建一个图表对象
chartObject = chartObjects.Add(left, top, width, height);
//关联图表对象
lpDisp = chartObject.get_Chart();
chart.AttachDispatch(lpDisp);
chart.put_ChartType(72); //设置图表类型
//设置数据的有效区域
lpDisp = wsMySheet.get_Range(COleVariant(_T("B1")), COleVariant(_T("C9")));
ASSERT(lpDisp);
VARIANT var;
var.vt = VT_DISPATCH;
var.pdispVal = lpDisp;
chart.ChartWizard(var, // Source.
covOptional, // Gallery
covOptional, // Format: 1~6.
COleVariant((short)2), // PlotBy: 指定系列中的数据是来自行(1)还是来自列(2).
COleVariant((short)0), // CategoryLabels.
COleVariant((short)1), // SeriesLabels.
COleVariant((short)TRUE), // HasLegend.
COleVariant(_T("chart title")), // Title.
COleVariant(_T("XXX")), // CategoryTitle.
COleVariant(_T("YYY")), // ValueTitles.
covOptional // ExtraTitle.
);
system("pause");
CString strSaveAsName = _T("C:\\new.xlsx");
wbMyBook.SaveCopyAs(_variant_t(strSaveAsName));
wbMyBook.put_Saved(TRUE);
ExcelApp.put_Visible(TRUE);
system("pause");
//释放资源
chart.ReleaseDispatch();
chartObject.ReleaseDispatch();
chartObjects.ReleaseDispatch();
rgMyRge.ReleaseDispatch();
wsMySheet.ReleaseDispatch();
wsMySheets.ReleaseDispatch();
wbMyBook.ReleaseDispatch();
wbsMyBooks.ReleaseDispatch();
ExcelApp.ReleaseDispatch();
ExcelApp.Quit();
4.参考文献
1.http://www.cnblogs.com/xianyunhe/archive/2011/09/25/2190485.html
2.http://www.cnblogs.com/yaowen/archive/2013/01/22/2870762.html
3.http://blog.csdn.net/stephen_kong/article/details/1362469
---------------------
作者:时光小偷的老巢
来源:CSDN
原文:https://blog.csdn.net/xiaoxiangyuan123456/article/details/70941588
版权声明:本文为博主原创文章,转载请附上博文链接!