[原创]在VC中彻底玩转Excel
本帖最后由 作者 于 2004-10-8 8:21:13 编辑 <br /><br /> 如今Excel是越来越重要了,在我们自己开发的程序中不免要和Excel打交道了。利用Automation技术,我们可以在不去了解<BR>数据库的情况下玩转Excel,而且你会发现一切竟如此轻松!<BR> 好了,咱们开始吧,我不喜欢用长篇累牍的代码来故弄玄虚,所以下面的代码都是切中要害的片段,总体上是个连贯的过程,<BR>包括启动Excel,读取数据,写入数据,以及最后的关闭Excel,其中还包括了很多人感兴趣的合并单元格的处理。<BR> 特别说明以下代码需要MFC的支持,而且工程中还要包含EXCEL2000的定义文件:EXCEL9.H,EXCEL9.CPP<BR><BR>*****************************************************************************************************************<BR><BR> //*****<BR> //变量定义<BR> _Application app; <BR> Workbooks books;<BR> _Workbook book;<BR> Worksheets sheets;<BR> _Worksheet sheet;<BR> Range range;<BR> Range iCell;<BR> LPDISPATCH lpDisp; <BR> COleVariant vResult;<BR> COleVariant<BR> covTrue((short)TRUE),<BR> covFalse((short)FALSE),<BR> covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); <BR> <BR> <BR> //*****<BR> //初始化COM的动态连接库<BR> if(!AfxOleInit()) <BR> {<BR> AfxMessageBox("无法初始化COM的动态连接库!");<BR> return ;<BR> } <BR> <BR> <BR> //*****<BR> //创建Excel 2000服务器(启动Excel)<BR> if(!app.CreateDispatch("Excel.Application")) <BR> {<BR> AfxMessageBox("无法启动Excel服务器!");<BR> return;<BR> }<BR> <BR> app.SetVisible(TRUE); //使Excel可见<BR> app.SetUserControl(TRUE); //允许其它用户控制Excel<BR> <BR><BR> //***** <BR> //打开c:\1.xls<BR> books.AttachDispatch(app.GetWorkbooks());<BR> lpDisp = books.Open("C:\\1.xls", <BR> covOptional, covOptional, covOptional, covOptional, covOptional,<BR> covOptional, covOptional, covOptional, covOptional, covOptional,<BR> covOptional, covOptional ); <BR> <BR> <BR> //*****<BR> //得到Workbook<BR> book.AttachDispatch(lpDisp);<BR> <BR> <BR> //*****<BR> //得到Worksheets <BR> sheets.AttachDispatch(book.GetWorksheets()); <BR> <BR> <BR> //*****<BR> //得到当前活跃sheet<BR> //如果有单元格正处于编辑状态中,此操作不能返回,会一直等待<BR> lpDisp=book.GetActiveSheet();<BR> sheet.AttachDispatch(lpDisp); <BR> <BR><BR> //*****<BR> //读取已经使用区域的信息,包括已经使用的行数、列数、起始行、起始列<BR> Range usedRange;<BR> usedRange.AttachDispatch(sheet.GetUsedRange());<BR> range.AttachDispatch(usedRange.GetRows());<BR> long iRowNum=range.GetCount(); //已经使用的行数<BR> <BR> range.AttachDispatch(usedRange.GetColumns());<BR> long iColNum=range.GetCount(); //已经使用的列数<BR> <BR> long iStartRow=usedRange.GetRow(); //已使用区域的起始行,从1开始<BR> long iStartCol=usedRange.GetColumn(); //已使用区域的起始列,从1开始<BR> <BR> <BR> //*****<BR> //读取第一个单元格的值<BR> range.AttachDispatch(sheet.GetCells()); <BR> range.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );<BR> COleVariant vResult =range.GetValue();<BR> CString str;<BR> if(vResult.vt == VT_BSTR) //字符串<BR> {<BR> str=vResult.bstrVal;<BR> }<BR> else if (vResult.vt==VT_R8) //8字节的数字 <BR> {<BR> str.Format("%f",vResult.dblVal);<BR> }<BR> else if(vResult.vt==VT_DATE) //时间格式<BR> {<BR> SYSTEMTIME st;<BR> VariantTimeToSystemTime(&vResult.date, &st);<BR> }<BR> else if(vResult.vt==VT_EMPTY) //单元格空的<BR> {<BR> str="";<BR> } <BR> <BR> <BR> //*****<BR> //读取第一个单元格的对齐方式,数据类型:VT_I4<BR> //读取水平对齐方式<BR> range.AttachDispatch(sheet.GetCells());<BR> iCell.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);<BR> vResult.lVal=0;<BR> vResult=iCell.GetHorizontalAlignment();<BR> if(vResult.lVal!=0)<BR> {<BR> switch (vResult.lVal)<BR> {<BR> case 1: //默认<BR> break;<BR> case -4108: //居中<BR> break;<BR> case -4131 : //靠左<BR> break;<BR> case -4152 : //靠右<BR> break;<BR> }<BR> <BR> }<BR> <BR> //垂直对齐方式<BR> iCell.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);<BR> vResult.lVal=0;<BR> vResult=iCell.GetVerticalAlignment();<BR> if(vResult.lVal!=0)<BR> {<BR> switch (vResult.lVal)<BR> {<BR> case -4160 : //靠上<BR> break;<BR> case -4108 : //居中<BR> break;<BR> case -4107 : //靠下<BR> break;<BR> }<BR> <BR> }<BR> <BR> <BR> //*****<BR> //设置第一个单元格的值"HI,EXCEL!"<BR> range.SetItem(COleVariant(1),COleVariant(1),COleVariant("HI,EXCEL!")); <BR> <BR><BR> //*****<BR> //设置第一个单元格字体颜色:红色<BR> Font font;<BR> range.AttachDispatch(sheet.GetCells());<BR> range.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);<BR> font.SetColor(COleVariant((long)0xFF0000)); <BR> <BR> <BR> //*****<BR> //合并单元格的处理<BR> //包括判断第一个单元格是否为合并单元格,以及将第一个单元格进行合并<BR> Range unionRange;<BR> range.AttachDispatch(sheet.GetCells()); <BR> unionRange.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );<BR> <BR> vResult=unionRange.GetMergeCells(); <BR> if(vResult.boolVal==-1) //是合并的单元格 <BR> {<BR> //合并单元格的行数 <BR> range.AttachDispatch (unionRange.GetRows ());<BR> long iUnionRowNum=range.GetCount (); <BR> <BR> //合并单元格的列数<BR> range.AttachDispatch (unionRange.GetColumns ());<BR> long iUnionColumnNum=range.GetCount (); <BR> <BR> //合并区域的起始行,列<BR> long iUnionStartRow=unionRange.GetRow(); //起始行,从1开始<BR> long iUnionStartCol=unionRange.GetColumn(); //起始列,从1开始<BR> <BR> }<BR> else if(vResult.boolVal==0) <BR> {//不是合并的单元格}<BR> <BR> //将第一个单元格合并成2行,3列<BR> range.AttachDispatch(sheet.GetCells()); <BR> unionRange.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );<BR> unionRange.AttachDispatch(unionRange.GetResize(COleVariant((long)2),COleVariant((long)3)));<BR> unionRange.Merge(COleVariant((long)0)); //合并单元格<BR> <BR> <BR> //*****<BR> //将文件保存为2.xls <BR> book.SaveAs(COleVariant("C:\\2.xls"),covOptional,covOptional, \<BR> covOptional,covOptional,covOptional,0,\<BR> covOptional,covOptional,covOptional,covOptional); <BR> <BR> <BR> //*****<BR> //关闭所有的book,退出Excel <BR> book.Close (covOptional,COleVariant(OutFilename),covOptional);<BR> books.Close(); <BR> app.Quit(); <BR> <BR>*****************************************************************************************************************<BR> <BR> 作者 :龚敏<BR> Email : <A href="mailto:truetable@truetable.com" target="_blank" >truetable@truetable.com</A><BR> 主页 :<A href="http://www.truetable.com/" target="_blank" >http://www.truetable.com</A><BR><BR> 先给适当奖励,本想加入精华,但那样刚到明经的朋友就看不到了,过段时间再说吧。 关于excel.h和excel.cpp,要注意版本问题.
比如对excel xp, 类库是直接包含在excel.exe中. 因此你只要用加入类(add class)的方法,直接选中excel.exe,并选择对话框中的常用的几个类(如Rang)就可以编程了. 千万不要选所有的类,否则太大了. 修改字体颜色的那段漏了一句,应为:
//*****
//设置第一个单元格字体颜色:红色
Font font;
range.AttachDispatch(sheet.GetCells());
range.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
font.AttachDispatch (range.GetFont ());
font.SetColor(COleVariant((long)0xFF0000)); 如果,程序既要安装在2000的计算机上,也可能安装在XP的机子上,有的用户还用97,(指的都是office),可能会出现版本问题------其中比较麻烦的是--在app.quit()后,仍旧存在excel进程,如果这样?怎么解决? 请教高手,能否用vb控制excel做成dll,在arx中进行调用,因为毕竟vb与excel亲切 zhengkuo发表于2003-12-2 10:50:00static/image/common/back.gif如果,程序既要安装在2000的计算机上,也可能安装在XP的机子上,有的用户还用97,(指的都是office),可能会出现版本问题------其中比较麻烦的是--在app.quit()后,仍旧存在excel进程,如果这样
兼容性问题:
office2002-office97是向下兼容的,只要你不使用office2002中的新特性,程序在这些office版本中都好用
Excel程序不能退出的问题:<BR> 1 . 不要使用#import导入类型库,如:#import "c:\excel\excel.olb"<BR> 2 . 程序结束时,确保所有IDispatch都释放了,如:app.ReleaseDispatch (); 還是不明白如何得到excel.cpp和excel.h這兩個文件,請指教 有版本问题时,可以加一个判断:1、首先通过 exlApp得到版本,比如9.0,10.0,11.0,10.0以后的版本注意open函数的参数为15个,即最后在增加两个covOptional,即可,另外,补充一下,判断当前是否有excel应用程序在运行,使之更舒服一些:
::CLSIDFromProgID(L"Excel.Application",&clsid); // from registry<BR> if(::GetActiveObject(clsid, NULL,&pUnk) == S_OK)
{<BR> VERIFY(pUnk->QueryInterface(IID_IDispatch,(void**) &pDisp) == S_OK);<BR> ExcelApp.AttachDispatch(pDisp);<BR> pUnk->Release();<BR> } <BR>else
{<BR> if(!ExcelApp.CreateDispatch("Excel.Application")) {<BR> AfxMessageBox("Excel program not found");<BR> return 0;<BR> }<BR><BR> } 高手,真的厉害