在VC中使用智能指针操作Excel

最近的一个工程 中,需要将数据导入Excel 表格中,项目经理知道我以前没有接触过操作Excel 的经验,于是给 了一段小程序给我,让我参考着做。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

  这段程序是使用智能指针操作Excel ,在网络上找了一个星期,居 然没有一片关于智能指针操作Excel 的文章,只有Automation 技术,而且所有介绍Automation 技 术的文章都是大同小异,并且代码多,说明少。没有任何帮助,光有一堆代码,对于理解和使用没有太大的帮助。在这样一个艰苦的条件下,我决定利用手中仅有的 工具:Microsoft Excel Visual Basic 参考 和 Microsoft Visual Studio 2005 的提示功能,摸索出一些利用智能指针操作Excel 的心得,写出来,既是一次总结,也 是一种分享,并且摸索还在继续,心得也还陆续会有。

  一、背景说明

  1 .Microsoft Excel Visual Basic 参考是提供给VB 程序员的一个操作Excel 的帮助,帮助中的对象、集 合、方法、常量都已经在COM 中实现,在VC 中可以找到对应的实体。

  2 .既然是智能指针,那么绝大多数的操作都是“->” , 然而,如果安装了Visual Assist X ,使用“->” 操作符的时候,是得不到任何 提示的。这个时候,如果需要查看提示,则可以先使用“.” 操作符,编译时再将“.” 改成“->” 。

  二、Excel 概念介绍从MFC 工程结 构的角度来看,Excel 属于多文档视图结构,一个应用程序包含若干个文档,称作工作簿,每个文档中包含若干个工作表。从智能 指针对象模型来看可以做如下划分:

  1 ._ApplicationPtr : 该对象即表示一个Excel 应用程序。

  2 . WorkbooksPtr :在 一个_ApplicationPtr 对象中,包含一个工作簿集合。

  3 . _WorkbookPtr :在 工作簿集合中包含若干的工作簿对象。一个工作簿对象对应一个xls 文件。

  4 . WorksheetsPtr : 在一个工作簿对象中,包含一个工作表集合。

  5 . _WorksheetPtr : 在工作表集合中包含若干个工作表对象,工作表对象是操作Excel 的基本单位。

  6 . Range :这是一个集合,工 作表中单元格的集合,控制对单元格的操作。

  三、准备工作1 . 加载动态库。
#define OFFICEXP 1
#define OFFICE2000 2
//
如果使用OFFICE2000 的内核,手动将此处改为 #define OFFICE_VER OFFICE2000
#define OFFICE_VER OFFICEXP
#define USE_PROGID 1
#define USE_LIBID 0
#define _M2STR(x) #x
#define M2STR(x) _M2STR(x)
#ifndef MSDLL_PATH
      
#if OFFICE_VER == OFFICEXP
       
#define _MSDLL_PATH "C:Program FilesCommon FilesMicrosoft SharedOffice11MSO.DLL"
      #elif  
OFFICE_VER == OFFICE2000
        #define  
_MSDLL_PATH "C:Program FilesMicrosoft OfficeOfficeMSO9.dll"
     
#endif
#else
      
#define _MSDLL_PATH M2STR(MSDLL_PATH)
#endif
#import _MSDLL_PATH rename("RGB", "MSRGB")
#ifdef VBE6EXT_PATH
     
#import M2STR(VBE6EXT_PATH)
#else
       #import "C:Program FilesCommon FilesMicrosoft SharedVBAVBA6VBE6EXT.OLB"

#endif
#if USE_PROGID
      #import "progid:Excel.Sheet" auto_search auto_rename rename_search_namespace("Office10")
#elif USE_LIBID
#import "libid:{00020813-0000-0000-C000-000000000046}" auto_search auto_rename version(1.3) lcid(0) no_search_namespace
#else
     
#ifndef MSEXCEL_PATH
         #if  
OFFICE_VER == OFFICEXP
           #define  
_MSEXCEL_PATH "C:Program FilesMicrosoft OfficeOffice11excel.exe"
         #elif  
OFFICE_VER == OFFICE2000
           #define  
_MSEXCEL_PATH "C:Program FilesMicrosoft OfficeOfficeexcel.exe"
         
#endif
     
#else
         #define _MSEXCEL_PATH  
M2STR(MSEXCEL_PATH)
      
#endif
     
#import _MSEXCEL_PATH auto_search auto_rename dual_interfaces
#endif
using namespace Excel;

  2 .初始化COM 组件。

  CoInitialize(NULL); 程序结束时记得释放资源CoUninitialize();

  四、正式开始

  1 .操作Excel ,首先要初始化一个 应用程序实例,代码如下:

  _ApplicationPtr pApp;

  pApp.CreateInstance(L"Excel.Application");pApp->PutVisible(0,VARIANT_TRUE);

  使用ADO 操作过数据库的人对代码的前两句不会感到陌生,初始化的 实例不同而已,而第三句,则是使Excel 应用程序显示出来,就像在“ 开始” 菜 单中运行Excel 一样,可以看到一个打开的Excel 程序,如果赋值VARIANT_FALSE 则 看不到应用程序,但是在任务管理器中已经创建了一个EXCEL 进程,这是前两句的功劳。程序结束前退出应用程序:pBook.PutSaved(0,VARIANT_TRUE);pApp->Quit();

  2 .在这个空白的应用实例中,需要创建一个工作簿(即文档)。代码 如下:WorkbooksPtr pBooks = pApp->GetWorkbooks();_WorkbookPtr pBook  = pBooks->Add((long)xlWorkbook); 前面讲过,在应用实例中 有一个工作簿集合,就算初始时集合是空的,它也是存在的,要创建一个工作簿,实际就是在这个集合中添加一个工作簿而已,第一句代码获得工作簿集合,第二句 添加一个工作簿,并返回新创建工作簿的指针。由于一个工作簿对应一个“xls” 文件,所以,大部分情况下我们在一个应用实例中 都只会创建一个工作簿,这和习惯有关,但不是绝对,如果添加了多个,可以使用_WorkbookPtr Workbooks::GetItem(const _variant_t & Index) 这个函数来获得每个工作簿的指针。

  通过Studio 的提示功能,我们看到Add 函 数的原型:Excel::_WorkbookPtr Excel::Workbooks::Add(const _variant_t & Template,long lcid = 0) 在Microsoft Excel 帮助中,从“ 方 法” ,“A” ,“Add” 找到“ 应用于 Workbooks 对象的 Add 方法” ,我们可以看到对第一个参数的说明 (前面说过,由于VC 缺少这类函数的说明,我们只能借助VB 了),这个说明对VC 同 样适用。小弟水平有限,没有弄清第二个参数的所以然,姑且使用默认值,在这里不影响对Excel 的操作,这个参数大概是与COM 机 制有关的某个东西吧。

3 .上一步使用xlWorkbook 参数添加了一个工作 簿,因此,这个工作簿中默认有一个工作表,同样的道理,在工作簿中有一个工作表集合,要操作工作表,首先得到工作表集合。SheetsPtr pSheets = pBook->GetWorksheets(); 可以这样_WorksheetPtr pSheet = pSheets->GetItem(1); 也可以这样_WorksheetPtr pSheet = pBook->GetActiveSheet(); 来得到默认创建的那个工作表。这是因为当前只有一个工组表,所以这个工组表就默 认为激活的工作表(在工作簿中只会有一个工作表处于激活状态,就是当前操作的这个工作簿)。

  如果工作簿中有多个工组表,还是需要通过pSheets->GetItem 函 数获得工作表对象。补充,有些操作可以在工作表处于非激活状态下进行,这样,使用pSheets->GetItem 获得 工作表对象即可对工作表操作,但是有些操作必须是工作表处于激活状态下进行,因此,获得工作表对象后,还需要pSheet->Activate(); 激 活它。

  给工作表重命名吧:pSheet->PutName("Exp One Sheet");// 如果运行有错,可以pSheet->PutName(L"Exp One Sheet") 插 入一个工作表,函数原型:
_WorksheetPtr Worksheets::Add(
const _variant_t &Before = vtMissing,
   // 在哪个工作表前插入
const _variant_t &After = vtMissing,    // 在哪个工作表后插入
const _variant_t &Count = vtMissing,    // 插入工组表个数
const _variant_t &Type = vtMissing)     // 插入工作表类型
有意思的是,不光这个函数,其他的有默认值的参数的默认值都是vtMissing 。

  遗憾的是我没能找到vtMissing 的具体说明,姑且先用着。看 下面的代码:

_WorksheetPtr pSheet = pSheets->GetItem(2);
VARIANT var;
   var.vt = VT_DISPATCH;var.pdispVal = pSheet;
pSheets->Add();          // 在第一个工作表之前插入一 个空白工作表

pSheets->Add(var);         // 在pSheet 工作表 之前插入一个空白工作表
pSheets->Add(vtMissing,var);    // 在pSheet 工作表之后插入一 个空白工作表
pSheets->Add(vtMissing,var,2);   // 在pSheet 工作表之后插入两个 空白工作表
这里仅仅涉及到_variant_t 类 型的使用,将不做说明。

  注意前两个参数的使用即可,其他类型的参数将报错。

  4 .下面将让你看到不同于C 风格的代码 操作工作表中的单元格。

pSheet->Range["A1"][vtMissing]->Value2 = "EXP A1";
pSheet->Range["A1"][vtMissing]->Interior->Color = RGB(255,0,0);
pSheet->Range["A1"][vtMissing]->Font->Name = L"
隶书 ";
pSheet->Range["A1"][vtMissing]->Font->FontStyle = L"Bold Italic";
pSheet->Range["A1"][vtMissing]->Font->Size = 10;

  如何?是不是有点像VB ?只需要赋值就能改变对象的属性,以上代码 等价于:

RangePtr range = pSheet->GetRange("A3",vtMissing);
range->PutValue2("EXP A3");
InteriorPtr interior = range->GetInterior();
interior->PutColor(RGB(255,0,0));
Excel::FontPtr font = range->GetFont();
font->PutName(L"
隶书 ");
font->PutFontStyle(L"Bold Italic");
font->PutSize(10);

  VC 的程序员看到这段代码是不是就觉得亲切了,这是COM 技 术的魔力,要想知道为何,自己研究COM 技术吧。对上面的代码只做两点说明:

  (1 )Range["A1"][vtMissing] 和GetRange("A3",vtMissing) 表 示获得A3 表示的单元格(用过Excel 的人都知道A3 表示什么),通 过集合的形式表现出来,该集合只有一个单元格。如果把vtMissing 换成B5 ,那么将获得一个 由A3 和B5 之间左右单元组成的集合。

  (2 )FontPtr 之前的Excel:: 是 必须的,缺少Excel:: 编译器会报错,提示不能确定是哪一个FontPtr ,因为在不同的命名 空间中存在若干个FontPtr 。也许还存在其他的类型会有这样的情况,因此,要特别留意命名空间的限制。

五、小经验1 .基于COM 技术,一般的,I = A->GetP() 可以等价成I = A->P ;A->PutP(I) 可 以等价成A->P = I 。

  2 .在Microsoft Excel Visual Basic 参考中查找对象,方法和属性的时候,可以基于第一点将VB 转换成VC 。 根据VB 提供的参数类型,在VC 中使用VARIANT 类型承载。

 

venky补充:

EXCEL文件的关闭:
 pThisWorkbook->Close();
 pThisWorkbooks->Close();

 pApplication->PutUserControl(VARIANT_FALSE);
 pApplication->Quit();

代码示例:

#include <stdafx.h>
#include <iostream>

#import "c:/Program Files/Common Files/Microsoft Shared/OFFICE11/mso.dll" rename("RGB", "MSRGB")

#import "c:/Program Files/Common Files/Microsoft Shared/VBA/VBA6/VBE6EXT.OLB" raw_interfaces_only, /
 rename("Reference", "ignorethis"), rename("VBE", "JOEVBE")

#import "d:/Program Files/Microsoft Office/OFFICE11/excel.exe" exclude("IFont", "IPicture") /
 rename("RGB", "ignorethis"), rename("DialogBox", "ignorethis"), rename("VBE", "JOEVBE"), /
 rename("ReplaceText", "JOEReplaceText"), rename("CopyFile","JOECopyFile"), /
 rename("FindText", "JOEFindText"), rename("NoPrompt", "JOENoPrompt")

using namespace Office;
using namespace VBIDE;
using namespace Excel ;

std::wstring GetAppPath();
_ApplicationPtr pApplication = NULL ;
WorkbooksPtr pThisWorkbooks = NULL;
_WorkbookPtr pThisWorkbook = NULL ;
_WorksheetPtr pThisWorksheet = NULL ;
SheetsPtr pThisSheets = NULL ;

COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);

Excel::XlFileFormat vFileFormat ;
Excel::XlSaveAsAccessMode vSaveAsAccessMode ;
Excel::XlSaveConflictResolution vSaveConflictResolution ;

bool ExcelInit()
{
 try{
   HRESULT hr = pApplication.CreateInstance("Excel.Application") ;

 }
 catch (_com_error& e)
 {
  AfxMessageBox(TEXT("无法启动 Excel服务器,软件需要安装OFFICE 2003!"));
  return false ;
 }

 std::wstring  path = GetAppPath();
 path += TEXT("SN.xls");

 try
 {
  pThisWorkbooks = pApplication->GetWorkbooks() ;
  pThisWorkbook = pThisWorkbooks->Open(path.c_str(), covOptional,
   covOptional, covOptional, covOptional, covOptional,
   covOptional, covOptional, covOptional, covOptional,
   covOptional, covOptional, covOptional, covOptional,
   covOptional
   );

  pThisSheets = pThisWorkbook->GetWorksheets() ;
  pThisWorksheet = pThisSheets->GetItem((short)1);
 }
 catch (_com_error& e)
 {
  AfxMessageBox(TEXT("无法启动Excel服务器,软件需要安装OFFICE 2003!"));
  return false ;  
 }
 
 return true ;

}

void ReadExcelFile(CString& strSN, const int& iColIndex)
{
 RangePtr pThisRange = NULL ;
 pThisRange = pThisWorksheet->GetRange("A1:A10000") ;
 COleVariant rValue;
 rValue = pThisRange->GetItem(iColIndex,1);
 rValue.ChangeType(VT_BSTR);
 strSN = CString(rValue.bstrVal);

// pThisRange->Release();    // 释放Range对象
 return ;
}

void SetExcelIndex(const int SNIndex)
{
 RangePtr pThisRange = NULL ;
 pThisRange = pThisWorksheet->GetRange("B1") ;
 CString strIndex;
 strIndex.Format(TEXT("%d"),SNIndex);

 pThisRange->PutItem(1, 1, LPCTSTR(strIndex)) ;

 vSaveAsAccessMode = xlNoChange ;
 vFileFormat = xlWorkbookNormal ;
 vSaveConflictResolution = xlLocalSessionChanges ;
 std::wstring  path = GetAppPath();
 path += TEXT("SN.xls");
 
 pThisWorkbook->Save();
// pThisRange->Release();    // 释放Range对象

 return ;

}

int GetExcelIndex()
{
 int iIndex;

 RangePtr pThisRange = NULL ;
 pThisRange = pThisWorksheet->GetRange("B1") ;
 COleVariant rValue;
 rValue = pThisRange->GetItem(1,1);
 rValue.ChangeType(VT_BSTR);
 CString strIndex = CString(rValue.bstrVal);
 if (!strIndex.IsEmpty())
  iIndex = _ttoi(strIndex.GetBuffer(0));

// pThisRange->Release();    // 释放Range对象

 return iIndex ;

}


void CloseExcel()
{
 pThisWorkbook->Close();
 pThisWorkbooks->Close();

 pApplication->PutUserControl(VARIANT_FALSE);
 pApplication->Quit();
}

在InitInstance()里加上这段代码:

if(FAILED(::CoInitialize(NULL)))
 {
  AfxMessageBox(TEXT("无法初始化 COM的动态连接库"));
  return FALSE ;
 }

在ExitInstance()里加上这段代码:

 CloseExcel();
 ::CoUninitialize();

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值