動化容器也只是麻煩,并不難。我們一起來試試看怎么樣?
要做Excel的OLE自動化,前提當(dāng)然是你得對Excel的可編程對象十分了解,
了解它的最好方法就是使用VBA。為了讓大家有個比較,我們將首先用一
段VB代碼來實現(xiàn)一個最簡單的Excel功能,再償試用VC來實現(xiàn)同樣的功能,
看看工作量的差別有多大。
用VB實現(xiàn)自動化容器的好處是你根本不用知道什么是自動化,這就是我最
喜歡的傻瓜用法。真的嗎?真的!不信我信就試試看!各位,請打開筆記
本,啟動VB6,建立一個標(biāo)準(zhǔn)工程,然后選擇“工程”->“引用”,再選
中“Microsoft Excel 8.0 Object Library”點OK,Excel對像庫就裝載
進(jìn)來了,就這么簡單!然后在窗體建立一個命令按鈕并輸入如下代碼:
Private Sub Command1_Click()
Dim xlsApp As Application
Dim xlsWorkSheet As Worksheet
Dim xlsRange As Range
' 第一段
Set xlsApp = CreateObject("Excel.Application.8")
xlsApp.Visible = True
'第二段
xlsApp.SheetsInNewWorkbook = 1
'第三段
With xlsApp.Workbooks
If (.Count = 0) Then
.Add
End If
End With
'第四段
Set xlsWorkSheet = xlsApp.Worksheets.Item(1)
xlsWorkSheet.Range("A1").Value = "用VB做,多簡單!"
End Sub
上述代碼啟動Excel并在A1單元格中輸入了幾個字,這可能是做這
個操作所用的最少代碼(省略了錯誤處理)。第一段建立了一個
Excel對象并將Excel程序的窗口顯示出來;第二段設(shè)置在一個新工
作簿中的工作表數(shù)為1;第三段檢查如果沒有可用的工作簿則添加一
個;第四段則向A1單元格里寫入了一句話。
快運行一遍程序看看,是不是很簡單?哈哈!別高興得太早,我們
要接觸VC做OLE自動化了!心臟不好的朋友請把速效救心丹放在身邊!
如果你接觸過OLE自動化,那一定知道IDispatch接口了,它是OLE自
動化的核心接口,恐怕這是至今為止最慢的一個接口了,呵呵!
微軟的軟件工程師們就是熱心腸,他們怕用MFC的廣大程序員累著,
專門做了一個類來封裝IDispatch接口,這個類就是COleDispatchDriver,
它包裝了IDispatch接口的復(fù)雜性,然而IDispatch復(fù)雜在什么地方
呢?到MSDN上查一下,也許你會注意到它的Invoke接口函數(shù)了?該
函數(shù)應(yīng)該算是IDispatch的核心函數(shù),因為它將負(fù)責(zé)所有從自動化對
象中調(diào)用方法、設(shè)置屬性值以及取得屬性值的所有操作。
也許你看到該函數(shù)的原型以后會很softly的說:“TNND!這參數(shù)也TMD
太多了!”是呀!要不然怎么說VC程序員的眼神兒要好呢!光對齊
參數(shù)就得要求你8.6的好眼睛,至于老花眼,干脆就不能做OLE程序,
更別提COM接口了!嘻嘻!下面我們仔細(xì)研究一下該函數(shù)吧:
dispIdMember:該參數(shù)是一個DISPID類型。它唯一地指定了待調(diào)用
的函數(shù)或?qū)傩浴R?,Invoke可不像我們這樣按名稱來確定一個
函數(shù)或一個屬性,它是拿這個參數(shù)做為函數(shù)指針數(shù)組的索引。如果
你只知道該函數(shù)的名字而不知道這個索引值怎么辦呢?不要緊,你
可以用IDispatch::GetIDsOfNames從函數(shù)名稱得到DISPID。怎么樣?
這個參數(shù)是不是很好填?
請注意Invoke的wFlags參數(shù),它可以是
DISPATCH_METHOD、
DISPATCH_PROPERTYGET、
DISPATCH_PROPERTYPUT
分別表示Invoke函數(shù)的工作性質(zhì)為:調(diào)用方法、取得屬性值、設(shè)置
屬性值。COleDispatchDriver將上述三個操作分成了三個函數(shù):
InvokeHelper、GetProperty和SetProperty,分解了Invoke函數(shù)的
功能。
另外,請注意pDispParams
參數(shù),為了支持有不同參數(shù)個數(shù)的不同方法,Invoke使用了參數(shù)數(shù)
組來批量的接受參數(shù),這個很苯的方法卻很實用,你覺得呢?
哇塞!要是這么詳細(xì)地說下去,我不得寫一本書?!得!我們還是
啟動VC玩兒點實用的吧!用向?qū)梢粋€Dialog base的框架,并畫
一個按鈕。接下來就是一堆復(fù)雜的工作了!
首先在stdafx.h里加入對IDispatch接口提供支持的頭文件:
#include 《afxDisp.h》
再在應(yīng)用程序類的InitInstance()函數(shù)里加入:
AfxOleInit(); // 初始化OLE Automation庫
我們應(yīng)該怎么引用Excel對象庫呢?按Ctrl+W啟動Class Wizard,
再選擇“Add Class...”->“From a type library...”,然后
選擇“c:\program files\microsoft office\office”目錄下的
“Excel8.olb”,隨后在“confirm classes”對話框的List box
中將列出所有Excel8對象庫中可用的對象。按住ctrl鍵再用箭頭
選中Sheets、_Application、_Worksheet、_Workbook、Workbooks、
Range幾個對象,按確定。哈!你看到在ClassView窗口中多出的
幾個類嗎?現(xiàn)在Excel就可以任由我們擺布啦!
好了,VC的靈話性在這兒將會體現(xiàn)出來了,我們生成的幾個Excel
對象有很多已定義好的函數(shù),但它們可能并不完全適合我們,哈!
我們可以跟據(jù)需要來修改這些函數(shù)!!當(dāng)然不能亂改,前提是我
們必須對Excel VBA有詳細(xì)的了解!
比如Workbooks類中的Add函數(shù)有一個Template參數(shù)用于指定模板文
件,但大多數(shù)情況我們用不著模板文件,怎么辦呢?你是否記得在
VBA中這個參數(shù)是一個可選項?那就改唄!我們可以用C++的特性,
重載這個函數(shù)!在左邊的Workbooks上右鍵->“Add member
function...”,然后在function type中輸入“LPDISPATCH”,在
fucntion declaration輸入“Add()”然后按OK。然后按如下方法
編輯該函數(shù),大家看到,我們只是將Template參數(shù)去掉了,然而
繼續(xù)保留對InvokeHelper的調(diào)用,請別忘記,MFC自動化類通常都
是從COleDispatchDriver派生的。
LPDISPATCH Workbooks::Add()
{
LPDISPATCH result;
InvokeHelper(0xb5, DISPATCH_METHOD, VT_DISPATCH,
(void*)&result, NULL);
return result;
}
為對話框上的按鈕生成一個函數(shù)吧,我們要寫VC代碼了!別忘了在
對話框的實現(xiàn)文件中加入#include"excel8.h"呀!
準(zhǔn)備好了嗎?代碼如下?。。。?!
void CExcelOleDlg::OnButton1()
{
LPDISPATCH pDispatch = NULL;
LPUNKNOWN pUnknown = NULL;
CLSID clsid;
_Application appExcel;
LPDISPATCH pWorkbooks = NULL;
Workbooks Workbooks;
LPDISPATCH pWorkbook = NULL;
LPDISPATCH pWorksheets = NULL;
Sheets Worksheets;
LPDISPATCH pWorksheet = NULL;
_Worksheet Worksheet;
LPDISPATCH pWorkRange = NULL;
Range WorkRange;
CLSIDFromProgID( L"Excel.Application.8", &clsid );
// 看指定的對象是否已經(jīng)運行
if ( SUCCEEDED(GetActiveObject(
clsid, NULL, &pUnknown )) )
{
VERIFY( SUCCEEDED(pUnknown->QueryInterface(
IID_IDispatch, (void**)&pDispatch )) );
ASSERT( pDispatch );
appExcel.AttachDispatch( pDispatch );
pUnknown->Release();
}
else
{
// 沒運行。那就建立Excel.Application.8對象
if ( ! appExcel.CreateDispatch(
(LPCTSTR)"Excel.Application.8") )
{
MessageBox(
"can not found the Excel.Application.8 object!");
return;
}
}
// 顯示并激活Excel窗口
// XLMAIN 是用Spy++查到的
HWND hWndExcelMain = ::FindWindow( "XLMAIN", NULL );
ASSERT( hWndExcelMain );
::ShowWindow( hWndExcelMain, SW_SHOW );
::UpdateWindow( hWndExcelMain );
::BringWindowToTop( hWndExcelMain );
// 設(shè)置在一個新工作簿中的工作表數(shù)為1
appExcel.SetSheetsInNewWorkbook( 1 );
// 得到工作簿集的IDispatch,并綁定到Workbooks對象
pWorkbooks = appExcel.GetWorkbooks();
ASSERT( pWorkbooks );
Workbooks.AttachDispatch( pWorkbooks );
// 如果工作簿集是空的,那就添加一個工作簿
if ( Workbooks.GetCount() == (long)0 )
{
pWorkbook = Workbooks.Add();
ASSERT( pWorkbook );
}
// 得到工作表集(同上)
pWorksheets = appExcel.GetWorksheets();
ASSERT( pWorksheets );
Worksheets.AttachDispatch( pWorksheets );
// 激活第一個工作表
pWorksheet = Worksheets.GetItem(
COleVariant((short)1) );
ASSERT( pWorksheet );
Worksheet.AttachDispatch( pWorksheet );
Worksheet.Select( COleVariant((short)TRUE) );
// 得到A1區(qū)域的引用
pWorkRange = Worksheet.GetRange(
COleVariant("A1"), COleVariant("A1"));
WorkRange.AttachDispatch( pWorkRange );
WorkRange.SetValue(
COleVariant("多簡單!就是麻煩一點兒!") );
if ( pWorkbook )
pWorkbook->Release();
}
請注意,上述代碼中除了了加入錯誤檢查之外,還判斷指定的對象是否已經(jīng)
運行,如果已經(jīng)運行則直接引用該對象,否則才建立新對象,其它的和上面
VB程序的功能相同!?。【谷欢嗔诉@么多代碼,真讓人不可思議!
快累死我了!我是實在沒力氣解釋這段VC程序了,感興趣的朋友可以拿它和
上面的VB程序?qū)φ找幌?,回頭再討論這個問題吧!?。」?--依呀累死我了?。?/p>