Excel开发文档
这篇文章的例子采用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/local/sampleexecutables/Technologies/Interop/Applications/Office/Excel.zip
MSND也包含了一个专题:Office Solutions Development。
Excel的Application对象
概念
Application对象代表的是Excel程序。为了更好的理解Application是什么,我们可以先启动一个Excel程序,然后选择菜单栏最右边的关闭按钮,这样就可以关掉默认创建的空文档对象。现在出现在我们眼前的就是Application对象:
Excel是一个MDI程序。MDI(Mutiple Document Interface—多文档界面)怎么理解呢?
熟悉微软历史悠久的MFC开发知识的程序员就知道:每一个MDI窗口应用程序都有一个主框架窗口,主框架窗口可以拥有多个子框架窗口,每个子框架窗口管理一个Document对象(文档对象负责管理数据)和一个View对象(视图对象负责显示数据,接受用户事件)。实际上后来MDI概念只是代表一种风格,即一个主框架窗口允许同时显示多个子窗口,是否有Document对象已经不重要。
现在我们可以清楚地知道Excel的Application对象就代表了MDI风格窗口的主框架。
示例
示例的目的是描述如何使用多种语言来创建一个Excel程序。为了简化篇幅,如何使用IDE的内容不作详细描述。
C#代码
首先创建一个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程序。就像前面的图示一样。
C++代码
创建一个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#复杂得多,主要在于:
- 引入组件库的时候需要重命名一些类,避免重名
- 错误信息的获取没有C#的COMException异常对象来支持,需要自己处理。CWindowsError::getOfficeError方法是我自己花了一个小时才编写好的。代码如下:
#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();
}
};
- 另外你需要对COM有所了解,这点对很多VC程序员难度都不小。
由于.Net是通过Interop方式间接调用Office组件(整个Office程序都是由COM组件编写的),因此比起C++直接调用IDispatch接口方式要慢得多。不过一般情况下,使用Office的程序性能要求不会很苛刻,.Net技术可以让我们的生活更加轻松许多。
Workbooks和Workbook对象
WorkBook对象代表了一个Excel程序可以打开的一个工作簿。如下图中标题为Book1的子窗口就是一个Workbook对象。
由于Excel是MDI程序,所以可以同时打开多个WorkBook对象作为子窗口。如下图中的Book1和Book2窗口。
代表框架窗口的Application对象管理着WorkBooks对象,WorkBooks对象是WorkBook对象的集合。
创建一个空Workbook对象
C#代码
我们对前面的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枚举类型的值指定了要创建的Workbook的类型。
xlWBATChart 代表Chart.
xlWBATExcelIntlMacroSheet 代表Excel version 4 macro.
xlWBATExcel4MacroSheet 代表Excel version 4 international macro.
xlWBATWorksheet 代表Worksheet.
Worksheet的概念下面一个章节会讲到,这里需要知道的是当创建一个WorkBook对象的时候,总是会自动创建一个Worksheet对象。
C++代码
_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
};
打开一个已经存在的WorkBook对象
C#代码
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);
}
C++代码
_WorkbookPtr openWorkbook(_ApplicationPtr app,string const& fileName)
{
WorkbooksPtr books=app->GetWorkbooks();
return books->Open(_bstr_t(fileName.c_str()));
}
WorkSheets和WorkSheet对象
每一个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 GetValue(Worksheet sheet, int row, int col)
{
Range cell=(Range)sheet.UsedRange.Cells[row, col];
return cell.Text.ToString();
}
注意,行和列的索引总是从1开始。
改写某表格指定位置的数据
static void SetValue(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