這篇文章的例子采用 Office 2003 英文版。首先打開一個 Excel2003 程序,然后選擇菜單 Help — Microsoft Excel Help, 如下圖:
這樣,右邊會出現(xiàn)一個幫助子窗口,如下:
選擇 Table of Contents ,會出現(xiàn)下圖。
最后一行 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 程序 ,然后選擇菜單欄最右邊的關閉按鈕,這樣就可以關掉默認創(chuàng)建的空文檔對象。 現(xiàn)在出現(xiàn)在我們眼前的就是 Application 對象:
Excel 是一個 MDI 程序。 MDI ( Mutiple Document Interface — 多文檔界面)怎么理解呢?
熟悉微軟歷史悠久的 MFC 開發(fā)知識的程序員就知道:每一個 MDI 窗口應用程序都有一個主框架窗口,主框架 窗口可以擁有多 個子框架窗口 ,每個子框架窗口管理一個 Document 對象 ( 文檔對象負責管理數(shù)據(jù) ) 和一個 View 對象(視圖對象負責顯示數(shù)據(jù),接受用戶事件)。 實際上后來 MDI 概念只是代表一種風格,即一個主框架窗口允許同時顯示多個子窗口 ,是否有 Document 對象已經(jīng)不重要。
現(xiàn)在我們可以清楚地知道 Excel 的 Application 對象就代表了 MDI 風格窗口的主框架。
示例的目的是描述如何使用多種語言來創(chuàng)建一個 Excel 程序。 為了簡化篇幅, 如何使用 IDE 的內(nèi)容不作詳細描述。
首先創(chuàng)建一個 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 (); 創(chuàng)建了一個Excel的Application對象。 app.Visible = true ; 設置窗口狀態(tài)為顯示。 app .Quit(); 關閉Application對象。注意,如果出錯會拋出COMException異常。 如果我們將斷點放在app.Quit()這一行,我們會看到程序會打開一個只有主框架的Excel程序。就像前面的圖示一樣。
創(chuàng)建一個 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 方式間接調(diào)用 Office 組件(整個 Office 程序都是由 COM 組件編寫的),因此比起 C++ 直接調(diào)用 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方法的實現(xiàn)代碼 , XlWBATemplate 枚舉類型 的值 指定了 要創(chuàng)建的 Workb ook的類型。
xlWBATChart 代表 Chart.
xlWBATExcel IntlMacroSheet 代表 Excel version 4 macro.
xlWBATExcel4MacroSheet 代表 Excel version 4 international macro.
xlWBATWorksheet 代表 Worksheet.
Worksheet 的概念下面一個章節(jié)會講到,這里需要知道的是當創(chuàng)建一個 WorkBook 對象的時候 , 總是會 自動 創(chuàng)建一個 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 指定行上面插入指定數(shù)量行)
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);
}