在COM中使用陣列引數-SafeArray (轉)

worldblog發表於2008-01-31
在COM中使用陣列引數-SafeArray (轉)[@more@]

在COM中使用陣列引數-SafeArray

關鍵字:DCOM、陣列、自定義型別、Marshal、SafeArray、ICollection

1  使用SafeArray

SafeArray是VB中的陣列方式。透過SafeArray,可以在VC++和VB間相互。SafeArray也是Automation中的標準陣列儲存方式。

1.1  SafeArray處理

COM提供了一套用於處理SafeArray。為了保證和SafeArray結構無關/editor/Editor.htm#_ftn1" name=_ftnref1>[1],程式中建立、讀取、更改和釋放SafeArray都應該透過這些API進行,而不應該直接讀寫SafeArray結構。

下面介紹常用的SafeArray處理函式。

1.1.1  建立SafeArray

SAFEARRAY* SafeArrayCreate(

  VARTYPE  vt,

  unsigned int cDims,

  SAFEARRRAYBOUND * rgsabound

);

 :namespace prefix = o ns = "urn:schemas--com::office" />

SAFEARRAY SafeArrayCreateEx(

  VARTYPE vt,

  unsigned int cDims,

  SAFEARRRAYBOUND * rgsabound

  PVOID  pvExtra

);

 

SAFEARRAY* SafeArrayCreateVector(

  VARTYPE vt,

  long lLbound,

  unsigned int cElements

);

 

SAFEARRAY* SafeArrayCreateVectorEx(

  VARTYPE vt,

  long lLbound,

  unsigned int cElements,

  LPVOID pvExtra

);

 

SafeArrayCreate於建立多維普通陣列。SafeArrayCreateEx用於建立多維自定義型別或介面指標陣列。SafeArrayCreateVector用於建立一維普通陣列。SafeArrayCreateVectorEx用於建立一維自定義型別或介面指標陣列。

1.1.2  釋放陣列

 

HRESULT SafeArrayDestroy(

  SAFEARRAY *  psa 

);

 

SafeArrayDestroy用於釋放建立的SafeArray陣列。

1.1.3  訪問資料

 

HRESULT SafeArrayAccessData(

  SAFEARRAY * psa,

  void HUGEP **  ppvData

);

 

HRESULT SafeArrayUnaccessData(

  SAFEARRAY * psa

);

 

SafeArrayAccessData函式返回陣列的指標。而SafeArrayUnaccessData釋放透過SafeArrayAccessData所取得的指標。

1.2  SafeArray相關處理

1.2.1  建立SafeArray陣列

建立SafeArray可以使用COM提供的四個建立函式之一。所有的建立函式都返回一個SafeArray指標。透過這個指標可以讀寫SafeArray中的資料。SafeArray使用完後必須釋放。

1.  SafeArrayCreateVector

 

SAFEARRAY* SafeArrayCreateVector(

  VARTYPE  vt, 

  long  lLbound, 

  unsigned int  cElements 

);

 

這個函式用來建立簡單型別的一維陣列。這個函式有三個引數:vt是陣列型別、lLbound是陣列下界值(最小下標)和陣列長度。vt的取值如下表:

vt值

型別

VT_UI1

無符號1位元組整數(BYTE)陣列

VT_UI2

無符號2位元組整數()陣列

VT_UI4

無符號4位元組整數(DWORD)陣列

VT_UINT

無符號整數(UINT)陣列

VT_INT

有符號整數(INT)陣列

VT_I1

有符號1位元組整數陣列

VT_I2

有符號2位元組整數陣列

VT_I4

有符號4位元組整數陣列

VT_R4

IEEE 4位元組浮點數(float)陣列

VT_R8

IEEE 8位元組浮點數(double)陣列

VT_CY

8位元組定點數貨幣值陣列

VT_BSTR

VB字串陣列

VT_DECIMAL

12位元組定點數(大數字)陣列

VT_ERROR

標準錯誤編號陣列

VT_BOOL

布林值陣列

VT_DATE

日期型陣列

VT_VARIANT

Variant型別陣列

lLbound是陣列的最小下標,可以是取負數。cElements是陣列的長度。陣列的最大下標的值是最小下標加上陣列長度減一。

SafeArrayCreateVector函式返回SafeArray結構的指標。

2.  SafeArrayCreateVectorEx

SAFEARRAY* SafeArrayCreateVectorEx(

  VARTYPE  vt, 

  long  lLbound, 

  unsigned int  cElements, 

  LPVOID  pvExtra 

);

 

這個函式用於建立自定義型別或COM的SafeArray陣列。和SafeArrayCreateVector類似,SafeArrayCreateVector也有型別、下界和長度的三個引數。SafeArrayCreateVectorEx還增加了一個引數pvExtra。

pvExtra的含義和vt的取值有關。當vt的取值在上表中的時候,pvExtra的取值沒有作用。當vt取值VT_RECORD時,SafeArrayCreateVectorEx返回一個自定義型別(結構structure或聯合union)的陣列。這時,pvExtra必須是一個指向IRecordInfo的指標。

當vt取值是VT_UNKNOWN或VT_DISPATCH時。pvExtra是一個指向IID(介面GUID)的指標。在目前的COM規範中,pvExtra只能是IID_IUnknown和IID_IDispatch。並且必須和vt的取值一致。

a.  建立自定義型別陣列

當vt是VT_RECORD時。pvExtra必須是一個IRecordInfo指標。絕大多數情況下,我們從TLB中取得自定義型別的IRecordInfo指標。以下是取得IRecordInfo的程式碼:

 

IRecordInfo * pRecordInfo;

hr = GetRecordInfoFromGuids(

LibID,

MajorVer,

MinorVer,

LOCALE_USER_DEFAULT,

  TypeGUID,

&pRecordInfo);

 

上述程式碼中,LibID是所TLB的GUID,MajorVer和MinorVer分別是TLB的主、次版本號,TypeGUID是自定義結構的GUID。

函式返回的是IRecordInfo介面的指標。

b.  建立COM物件陣列

當需要建立COM陣列時,可以使用IUnknown指標,也可以用IDispatch指標。如果需要使用其它指標型別,應該使用QueryInterface方法取得,而不能直接在陣列中儲存。因為SafeArray陣列的序列化程式只能處理IUnknown和IDispatch兩種指標型別,如果在陣列中放其它介面型別的指標,可能在跨套間使用中會出現問題。

1.2.2  讀取和寫入SafeArray陣列。

讀寫SafeArray陣列時。應該使用COM提供的標準API。COM提供了大量函式用於SafeArray陣列的操作,本文中僅使用其中的兩個函式,SafeArrayAccessData和SafeArrayUnaccessData,和一些輔助用的函式。實際上是用這兩個函式就可以進行所有的陣列操作了。其它的函式用於對單個元素的操作,由於使用不多,而且也不高,所以本文中不進行說明。

1.  SafeArrayAccessData

這個函式用於獲取SafeArray的資料指標,並鎖定SafeArray陣列的資料。在取得了資料指標之後,就可以直接訪問SafeArray陣列中的資料了。

如果陣列型別是Type,那麼所取得的資料指標實際上就是Type型別的陣列的地址。在多維陣列的情況下,必須把多個維度的下標轉換成一維下標進行訪問。

2.  SafeArrayUnaccessData

這個函式的作用是對SafeArray資料解鎖,解鎖後,就不應該繼續對資料指標進行讀寫訪問。如果要訪問,必須重新獲取並鎖定資料。

3.  確定陣列結構

在訪問陣列之前,必須知道陣列中資料的型別,、維數以及每個維度的下界和長度。COM提供了取得這些陣列引數的函式。

取得型別,返回“VT_”開頭的型別列舉值:

 

HRESULT SafeArrayGetVartype (

  SAFEARRAY * pSA,

  VARTYPE * pVarType);

 

取得維數,返回陣列的維數:

 

UINT SafeArrayGetDim (

  SAFEARRAY * pSA);

 

取得每個維度的屬性,返回指定維數(nDim)的上界和下界(nDim從1開始):

 

HRESULT SafeArrayGetLBound (

  SAFEARRAY * pSA,

  UINT nDim,

  long * pLBound);

 

HRESULT SafeArrayGetUBound (

  SAFEARRAY * pSA,

  UINT nDim,

  long * pUBound);

 

取得自定義型別介面,對於自定義結構陣列,返回自定義結構型別資料的指標:

 

HRESULT SafeArrayGetRecordInfo (

  SAFEARRAY * pSA,

  IRecordInfo ** ppRecordInfo);

 

4.  訪問普通一維陣列

從SafeArrayAccessData返回的指標實際上就是C語言中的一維陣列地址。在VC++中可以像訪問普通陣列一樣讀寫這個陣列。

需要注意的是,在C語言中,所有的陣列下標都是從0開始的。而在SafeArray中,陣列下標可以從任何數字開始。所以在訪問前必須進行轉換。轉換方法就是從SafeArray的下標中減去陣列的下界,就可以得到C語言中陣列的下標了。

如下:

 

Type * pData;

long LBound;

SafeArrayAccessData(pSA, (void HUGEP **) &pData);

SafeArrayGetLBound(pSA, 1, &LBound);

Type Item = pData[n – LBound];

 

5.  訪問多維陣列

訪問多維陣列和訪問一維陣列類似,只是要把多維下標轉換成一維下標。把多維下標轉換成一維下標的方法和在陣列指標中介紹的是相似的。

設:有n個維度,每個維度的長度(上界減去下界加一)分別是L1、L2、…、Ln。要轉換的下標是X1、X2、…、Xn。可以根據下述公式轉換成一維陣列的下標。

X1+X2*L1+X3*(L1*L2)+X4*(L1*L2*L3)+…+Xn*(L1*L2*…*L(n-1))

6.  訪問自定義結構陣列

訪問自定義結構陣列的時候,可以使用#iimport自動生成或者IDL編譯產生的型別定義。如果沒有辦法取得自定義結構的宣告,可以使用IRecordInfo介面中的方法間接訪問自定義結構。

首先需要取得自定義結構的長度,這可以透過IRecordInfo::GetSize方法取得。

訪問自定義結構中的欄位內容,透過IRecordInfo::GetField和IRecordInfo::PutField方法實現。

透過IRecordInfo中的其它方法還可以取得每個欄位的屬性內容。大家可以參考相關文件。

1.2.3  釋放SafeArray陣列

釋放SafeArray陣列應該透過COM的支援函式:

 

HRESULT SafeArrayDestroy(SAFEARRAY * pSA);

 

1.3  使用SafeArray的IDL定義

每個介面都要透過IDL生成和佔位程式程式碼。為了使代理和佔位程式能夠正確地對引數進行序列化,必須正確的書寫IDL定義。

MIDL工具直接支援SafeArray型別資料的傳遞。但是,在傳遞SafeArray資料的時候,必須透過SAFEARRAY的指標進行。困難在於,VC++ 6.0的新增方法和新增屬性的工具不能夠正確的處理SafeArray陣列的情況。

 

在IDL中,陣列必須指定型別,如下:

 

[id(10)] HRESULT Foo([in] SAFEARRAY(LONG) pParam);

 

在實現的函式宣告中,要使用相應的指標型別:

 

HRESULT Foo(SAFEARRAY * pParam);

 

輸出和輸入輸出型別的陣列引數,在IDL中必須使用指標引數,而在函式宣告中則是雙重指標。

 

[id(11)] HRESULT Foo2([out] SAFEARRAY(LONG) * ppParam);

 

函式宣告如下:

 

HRESULT Foo2(SAFEARRAY ** ppParam);

 

1.4  VARIANT和SafeArray

在VB的介面中,經常透過VARIANT傳遞陣列引數。這裡簡述一下使用VARIANT引數傳遞陣列中需要注意的地方。

1.4.1  輸入陣列

對於輸入陣列,可以使用VARIANT指標,也可以使用VARIANT型別引數。在這兩種情況下,VARIANT中的型別是不同的。

當使用VARIANT指標時,輸入的VARIANT引數的型別(vt引數的值)是VT_ARRAY | VT_BYREF | VT_xxx。此時,使用VARIANT引數的pparray欄位取得SafeArray指標。

如果引數是VARIANT,輸入的VARIANT引數的型別(vt引數的值)是VT_ARRAY | VT_xxx。使用VARIANT引數的parray欄位取得SafeArray指標。

必須注意這兩種情況下,VARIANT的型別不同,所以程式碼也會有區別。

1.4.2  輸出陣列

輸出和輸入輸出陣列,必須使用VARIANT指標,這時,VARIANT型別是VT_ARRYA | VT_BYREF | VT_xxx。

1.5  SafeArray管理

使用COM專用的建立和銷燬API函式處理SafeArray。

對於輸入型的SafeArray,呼叫方負責建立和銷燬SafeArray;對於輸出型的SafeArray,由被呼叫方建立,呼叫方銷燬;輸入輸出型SafeArray,呼叫方建立,被呼叫方可以銷燬並重新建立,最終由呼叫方銷燬。

 



這樣就可以在修改SafeArray結構時,保證程式相容性。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-998947/,如需轉載,請註明出處,否則將追究法律責任。

相關文章