这篇文章的例子采用 Office 2003 英文版。首先打开一个 Excel2003 程序,然后选择菜单 Help — Microsoft Excel Help, 如下图:
这样,右边会出现一个帮助子窗口,如下:
选择 Table of Contents ,会出现下图。
最后一行 Microsoft Excel Visual Basic Reference 就是我们要找的文档。该文档基本描述了 Excel 的主要对象的属性和方法。
如果你安装了 MSDN FOR VS.NET 2005 英文版 , 你可以在下面的地址找到 Excel 的例子程序:
ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_fxsamples/l ocal/sampleexecutables/Technologies/Interop/Applications/Office/Excel.zip
MSND 也包含了一个专题: Office Solutions Development 。
Application 对象代表的是 Excel 程序。 为了更好的理解 Application 是什么,我们可以先启动一个 Excel 程序 ,然后选择菜单栏最右边的关闭按钮,这样就可以关掉默认创建的空文档对象。 现在出现在我们眼前的就是 Application 对象:
Excel 是一个 MDI 程序。 MDI ( Mutiple Document Interface — 多文档界面)怎么理解呢?
熟悉微软历史悠久的 MFC 开发知识的程序员就知道:每一个 MDI 窗口应用程序都有一个主框架窗口,主框架 窗口可以拥有多 个子框架窗口 ,每个子框架窗口管理一个 Document 对象 ( 文档对象负责管理数据 ) 和一个 View 对象(视图对象负责显示数据,接受用户事件)。 实际上后来 MDI 概念只是代表一种风格,即一个主框架窗口允许同时显示多个子窗口 ,是否有 Document 对象已经不重要。
现在我们可以清楚地知道 Excel 的 Application 对象就代表了 MDI 风格窗口的主框架。
示例的目的是描述如何使用多种语言来创建一个 Excel 程序。 为了简化篇幅, 如何使用 IDE 的内容不作详细描述。
首先创建一个 C# 的 Console 工程。我这里使用的总是 Visual Studio.net 2005 英文版。 然后右键选择工程,选择 Add Reference ,在弹出的对话框中选择 COM 一栏,选中如下的组件:
请注意下面的代码:
using System.Reflection; // For Missing.Value and BindingFlags
using System.Runtime.InteropServices; // For COMException
using Microsoft.Office.Interop.Excel;
namespace ExcelApplicationSample
{
class Program
{
static void Main ( string [] args)
{
try
{
Application app = new Application ();
app.Visible = true ;
app.Quit();
app=null; // 这句话可以使垃圾回收器关闭Excel进程
}
catch ( COMException e)
{
Console .WriteLine(e.Message);
}
}
}
}
Application app = new Application (); 创建了一个Excel的Application对象。 app.Visible = true ; 设置窗口状态为显示。 app .Quit(); 关闭Application对象。注意,如果出错会抛出COMException异常。 如果我们将断点放在app.Quit()这一行,我们会看到程序会打开一个只有主框架的Excel程序。就像前面的图示一样。
创建一个 Win32 Console 工程 ExcelApplicationSampleCPlus 。然后选择添加 ATL 支持,如下图:
源代码如下:
#include "stdafx.h"
#include
using namespace std;
#import "C:Program FilesCommon FilesMicrosoft SharedOFFICE11mso.dll" rename( "RGB" , "MSRGB" )
#import "C:Program FilesCommon FilesMicrosoft SharedVBAVBA6VBE6EXT.OLB"
rename( "Reference" , "ignorethis" ), rename( "VBE" , "JOEVBE" )
#import "C:Program FilesMicrosoft OfficeOFFICE11excel.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 ;
#include "WindowsError.h"
class AppartmentWrapper
{
public :
AppartmentWrapper()
{
::CoInitialize(NULL);
}
~AppartmentWrapper()
{
::CoUninitialize();
}
};
int _tmain( int argc, _TCHAR* argv[])
{
try
{
AppartmentWrapper appartment;
_ApplicationPtr ptr=NULL;
HRESULT hr=ptr.CreateInstance( "Exce2l.Application" );
if (FAILED(hr))
{
cout<<
return 1;
}
ptr->PutVisible (0,VARIANT_TRUE);
hr=ptr->Quit();
if (FAILED(hr))
{
cout<<
return 1;
}
}
catch (_com_error const & e)
{
cout<<
return 1;
}
return 0;
}
VC++ 的代码要比 C# 复杂得多,主要在于:
#pragma once
#include
#include
class CWindowsError
{
public :
static std::string getLastError()
{
char szBuf[80];
void * lpMsgBuf=NULL;
DWORD dw = GetLastError();
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
( char *) &lpMsgBuf,
0,
NULL);
wsprintfA(szBuf, "error %d: %s" ,dw, lpMsgBuf);
LocalFree(lpMsgBuf);
return szBuf;
}
static std::string getOfficeError(HRESULT hr)
{
char buf[256]={0};
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_ NEUTRAL, SUBLANG_DEFAULT),
&buf[0],
256,
NULL);
std::stringstream stream;
stream<< "error " <
<< ": " <return stream.str();
}
};
由于 .Net 是通过 Interop 方式间接调用 Office 组件(整个 Office 程序都是由 COM 组件编写的),因此比起 C++ 直接调用 IDispatch 接口方式要慢得多。不过一般情况下,使用 Office 的程序性能要求不会很苛刻, .Net 技术可以让我们的生活更加轻松许多。
WorkBook 对象代表了一个 Excel 程序可以打开的一个 工作簿。如下图中标题为 Book1 的子窗口就是一个 Workbook 对象。
由于 Excel 是 MDI 程序,所以可以同时打开多个 WorkBook 对象作为子窗口。如下图中的 Book1 和 Book2 窗口。
代表框架窗口的 Application 对象管理着 WorkBooks 对象, WorkBooks 对象是 WorkBook 对象的集合。
我们对前面的 C# 代码进行了 一些修改,代码如下:
class Program
{
static void Main ( string [] args)
{
Application app= null ;
try
{
app = new Application ();
Workbook book=CreateDocument(app);
app.Visible = true ;
}
catch ( COMException e)
{
Console .WriteLine(e.Message);
}
finally
{
app.Quit();
}
}
static Workbook CreateDocument( Application app)
{
return app.Workbooks.Add( XlWBATemplate .xlWBATWorksheet);
}
}
注意CreateDocument方法的实现代码 , XlWBATemplate 枚举类型 的值 指定了 要创建的 Workb ook的类型。
xlWBATChart 代表 Chart.
xlWBATExcel IntlMacroSheet 代表 Excel version 4 macro.
xlWBATExcel4MacroSheet 代表 Excel version 4 international macro.
xlWBATWorksheet 代表 Worksheet.
Worksheet 的概念下面一个章节会讲到,这里需要知道的是当创建一个 WorkBook 对象的时候 , 总是会 自动 创建一个 Worksheet 对象。
_WorkbookPtr createWorkbook(_ApplicationPtr app)
{
WorkbooksPtr books=app->GetWorkbooks();
_variant_t v(xlWorksheet);
return books->Add(v);
}
int _tmain( int argc, _TCHAR* argv[])
{
try
{
AppartmentWrapper appartment;
_ApplicationPtr ptr=NULL;
HRESULT hr=ptr.CreateInstance( "Excel.Application" );
if (FAILED(hr))
{
cout<< CWindowsError::getOfficeError(hr)<<endl;
return 1;
}
_WorkbookPtr workbook=createWorkbook(ptr);
ptr->PutVisible (0,VARIANT_TRUE);
hr=ptr->Quit();
if (FAILED(hr))
{
cout<<CWindowsError::getOfficeError(hr)<<endl;
return 1;
}
}
catch (_com_error const & e)
{
cout<<CWindowsError::getOfficeError(hr)<<endl;
return 1;
}
return 0;
}
C++ 中的 Workbook 类型的枚举定义为:
enum XlSheetType
{
xlChart = -4109,
xlDialogSheet = -4116,
xlExcel4IntlMacroSheet = 4,
xlExcel4MacroSheet = 3,
xlWorksheet = -4167
};
static Workbook OpenDocument( Application app, String fileName)
{
return app.Workbooks.Open(fileName, Type .Missing, Type .Missing, Type .Missing, Type .Missing, Type .Missing, Type .Missing, Type .Missing,
Type .Missing, Type .Missing, Type .Missing, Type .Missing, Type .Missing, Type .Missing, Type .Missing);
}
_WorkbookPtr openWorkbook(_ApplicationPtr app,string const & fileName)
{
WorkbooksPtr books=app->GetWorkbooks();
return books->Open(_bstr_t(fileName.c_str()));
}
每一个 Workbook 对象都拥有一个或者多个 Worksheet 对象。每个 Worksheet 对象代表了一张表格。如下图:
这里有 Sheet1,Sheet2,Sheet3 三张表格, 他们都是 Worksheet 对象。 当前的 Workbook 对象代表了这个子窗口,并且用有成员 Worksheets 对象。 Worksheets 对象是三个 Worksheet 对象的集合。
Worksheet sheet = ( Worksheet )book.Sheets[ "Sheet1" ];
int rowCount=sheet.UsedRange.Rows.Count;
int colCount = sheet.UsedRange.Columns.Count;
static String G etValue( Worksheet sheet, int row, int col)
{
Range cell=( Range )sheet.UsedRange.Cells[row, col];
return cell.Text.ToString();
}
注意,行和列的索引总是从1开始。
static void S etValue( Worksheet sheet, int row, int col, String value)
{
Range cell = ( Range )sheet.UsedRange.Cells[row, col];
cell.Value2 = value; ;
}
// 插行(在指定 WorkSheet 指定行上面插入指定数量行)
static void InsertRows(Excel. Worksheet wst, int rowIndex, int count)
{
Excel. Range range = (Excel. Range )wst.Rows[rowIndex, Type .Missing];
for ( int i = 0; i < count; i++)
{
range.Insert(Excel. XlDirection .xlDown, Type .Missing);
}
}
5