||
BSTR是COM中的數(shù)據(jù)類型,在COM編程時(shí),接口中定義的字符串類型都是BSTR類型,
而使用BSTR類型是極其容易出錯(cuò)的,同時(shí),一不小心就有可能造成內(nèi)存泄露。所以有如下建議:
既然是這樣,就有人要問了,那么BSTR存在的必要是什么?
這個(gè)是由COM決定的,由于COM是跨系統(tǒng)及不同開發(fā)語(yǔ)言間實(shí)現(xiàn)互操作的技術(shù),常規(guī)以NULL結(jié)尾的簡(jiǎn)單字符串在COM組件間傳遞不太方便。
所以,BSTR就這么出現(xiàn)了。BSTR作為指針類型,標(biāo)準(zhǔn)的BSTR是一個(gè)有長(zhǎng)度前綴和NULL結(jié)束符的OLECHAR數(shù)組。BSTR的前4個(gè)字節(jié)是一個(gè)
表示字符串長(zhǎng)度的前綴。BSTR長(zhǎng)度域的值是字符串的字節(jié)數(shù),但不包括字符串結(jié)束符。BSTR實(shí)際上包含的是Unicode串,
所以字符數(shù)是字節(jié)數(shù)的一半。
所以,在能不使用BSTR的情況下,就盡量不要使用BSTR類型,而是使用對(duì)應(yīng)的_bstr_t類型。為了處理BSTR,Microsoft提供了以下API供使用:
BSTR SysAllocString(const OLECHAR * psz);VARIANT結(jié)構(gòu)體主要是使用在COM(組件對(duì)象模型)中用于傳遞參數(shù)使用,
它的存在主要是為了保持一個(gè)在COM參數(shù)傳遞方法的統(tǒng)一性,它幾乎包含了所有普通常用類型的數(shù)據(jù)類型的傳遞,
如整型,浮點(diǎn)型,布爾型等等,以及相應(yīng)類型的指針類型,如整型指針。
它的使用也比較方便。先來(lái)看看這個(gè)結(jié)構(gòu)體它的結(jié)構(gòu):
typedef struct tagVARIANT {
union {
struct __tagVARIANT {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
LONGLONG llVal;
LONG lVal;
BYTE bVal;
SHORT iVal;
FLOAT fltVal;
DOUBLE dblVal;
VARIANT_BOOL boolVal;
_VARIANT_BOOL bool;
SCODE scode;
CY cyVal;
DATE date;
BSTR bstrVal;
IUnknown *punkVal;
IDispatch *pdispVal;
SAFEARRAY *parray;
BYTE *pbVal;
SHORT *piVal;
LONG *plVal;
LONGLONG *pllVal;
FLOAT *pfltVal;
DOUBLE *pdblVal;
VARIANT_BOOL *pboolVal;
_VARIANT_BOOL *pbool;
SCODE *pscode;
CY *pcyVal;
DATE *pdate;
BSTR *pbstrVal;
IUnknown **ppunkVal;
IDispatch **ppdispVal;
SAFEARRAY **pparray;
VARIANT *pvarVal;
PVOID byref;
CHAR cVal;
USHORT uiVal;
ULONG ulVal;
ULONGLONG ullVal;
INT intVal;
UINT uintVal;
DECIMAL *pdecVal;
CHAR *pcVal;
USHORT *puiVal;
ULONG *pulVal;
ULONGLONG *pullVal;
INT *pintVal;
UINT *puintVal;
struct __tagBRECORD {
PVOID pvRecord;
IRecordInfo *pRecInfo;
} __VARIANT_NAME_4;
} __VARIANT_NAME_3;
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
} VARIANT, *LPVARIANT, VARIANTARG, *LPVARIANTARG;
這個(gè)結(jié)構(gòu)體呢,有5個(gè)成員,分別是 VARTYPE vt ,WORD wReserved1,WORD wReserved2,WORD wReserved3,和最后一個(gè)共用體。
其中vt用以指明最后一個(gè)共用體中哪一個(gè)成員有效,wReserved1,wReserved2,wReserved3,這三個(gè)我們使用的時(shí)候不用管,系統(tǒng)保留,
最后一個(gè)共用體根據(jù)vt的提示,對(duì)相應(yīng)的成員進(jìn)行值的存儲(chǔ)。我們從兩個(gè)不同的角度來(lái)理解,首先是使用VARIANT來(lái)存儲(chǔ)參數(shù),
首先是聲明一個(gè)這個(gè)結(jié)構(gòu)體的對(duì)象,然后對(duì)對(duì)象的vt進(jìn)行賦值,它可接受的值是一個(gè)枚舉值,也就說只能在枚舉這個(gè)范圍內(nèi)取值,
比如我要用VARIANT傳遞一個(gè)整數(shù),現(xiàn)在我對(duì)vt的賦值為VT_INT,這樣就說明了我要使用這個(gè)結(jié)構(gòu)體中共用體的整型變量,
接著對(duì)INT變量進(jìn)行賦值,賦我們要傳遞的值。這樣就完成VARIANT的傳遞。現(xiàn)在我們從另外一個(gè)角度來(lái)理解VARIANT,
剛才是我們對(duì)VARIANT對(duì)象進(jìn)行賦值傳遞,現(xiàn)在我們是這個(gè)VARIANT對(duì)象的接收者,我們從參數(shù)中獲得這個(gè)對(duì)象之后,
我們首先檢查這個(gè)結(jié)構(gòu)體的vt成員,看它哪個(gè)類型的變量有效,比如就這個(gè)例子而言,檢查到vt的值是VT_INT,
因此,我直接去獲取這個(gè)結(jié)構(gòu)體中VT_INT所對(duì)應(yīng)的變量,獲取它的值。這樣,我們從傳遞到使用兩個(gè)角度來(lái)理解了VARIANT結(jié)構(gòu)體,
概括起來(lái)說,就是vt指明了我要傳遞的變量的類型,結(jié)構(gòu)體中共用體的成員用來(lái)存儲(chǔ)vt指明的類型的值。
下面來(lái)看看具體VARIANT結(jié)構(gòu)體是如何使用賦值的,首先是第一種方法:
首先我們聲明一個(gè)VARIANT結(jié)構(gòu)體的對(duì)象vr1,然后使用VariantInit函數(shù)對(duì)其進(jìn)行初始化,它的作用就是對(duì)vt賦VT_EMPTY,
對(duì)別的變量值附空,否則就是一個(gè)隨機(jī)值,這個(gè)過程和我們聲明一個(gè)int變量一樣,如果聲明的時(shí)候不初始化,就是一個(gè)隨機(jī)值。
編程過程中,不管是指針還是什么變量,都應(yīng)該在聲明之后對(duì)其進(jìn)行初始化。接著就是我賦VT_INT給vt,
這里的V_VT就是代表要對(duì)vr1結(jié)構(gòu)體中的vt成員進(jìn)行幅值,接著對(duì)vr1中的INT成員賦值,這里的V_INT就是表示要對(duì)INT賦值,
因此這里有一個(gè)規(guī)律,就是V_,它的后面加成員類型就可以對(duì)相應(yīng)的成員賦值,而這里的成員類型有一個(gè)對(duì)照表,在文章的最后給出,
比如,我要對(duì)BSTR成員賦值,我就用V_BSTR。這時(shí)候,就可以使用vr1了,使用完成之后,我們應(yīng)該調(diào)用VariantClear函數(shù),
這個(gè)函數(shù)的作用就是將vt賦值為VT_EMPTY,以及釋放使用這個(gè)結(jié)構(gòu)體中的內(nèi)存中的內(nèi)容,如果是com對(duì)象,該函數(shù)是不會(huì)進(jìn)行對(duì)象的release操作的,
不管怎么樣,我們都應(yīng)該在使用完了VARIANT結(jié)構(gòu)體之后,調(diào)用這個(gè)函數(shù)。
下面是第二種方法,原理和過程和地中方法類似,有一點(diǎn)微小的區(qū)別,
就是VARIANT結(jié)構(gòu)體的賦值上面來(lái)說,有點(diǎn)不同,如下:
接下來(lái)是字符串的操作,BSTR,我們不能直接將字符串傳遞一個(gè)VARIANT結(jié)構(gòu)體對(duì)象,而是要用到函數(shù)SysAllocString,
它的返回值就是一個(gè)BSTR,由它分配一個(gè)字符串,供VARIANT,對(duì)于為什么要這么做,可以自己查看MSDN COM的Automation那部分:
完成之后,我們還應(yīng)該調(diào)用SysFreeString來(lái)釋放由SysAllocString分配的內(nèi)存。
另外,我們可以在類型與變量的對(duì)照表中發(fā)現(xiàn),同一類型,對(duì)應(yīng)了兩種不同的變量,如,INT對(duì)應(yīng)了變量有intVal和pintVal,其實(shí)這很簡(jiǎn)單,后者是指針性,如果要使用,說明我們賦值的對(duì)象是一個(gè)指針,如下:
最后給出類型與變量的對(duì)照表,如果是使用地中方法用V_加類型,就直接使用下表中VT_后的名稱就可以了:
Member name | Description |
---|---|
VT_EMPTY | Indicates that a value was not specified. |
VT_NULL | Indicates a null value, similar to a null value in SQL. |
VT_I2 | Indicates a short integer. |
VT_I4 | Indicates a long integer. |
VT_R4 | Indicates a float value. |
VT_R8 | Indicates a double value. |
VT_CY | Indicates a currency value. |
VT_DATE | Indicates a DATE value. |
VT_BSTR | Indicates a BSTR string. |
VT_DISPATCH | Indicates an IDispatch pointer. |
VT_ERROR | Indicates an SCODE. |
VT_BOOL | Indicates a Boolean value. |
VT_VARIANT | Indicates a VARIANT far pointer. |
VT_UNKNOWN | Indicates an IUnknown pointer. |
VT_DECIMAL | Indicates a decimal value. |
VT_I1 | Indicates a char value. |
VT_UI1 | Indicates a byte . |
VT_UI2 | Indicates an unsigned short . |
VT_UI4 | Indicates an unsigned long . |
VT_I8 | Indicates a 64-bit integer. |
VT_UI8 | Indicates an 64-bit unsigned integer. |
VT_INT | Indicates an integer value. |
VT_UINT | Indicates an unsigned integer value. |
VT_VOID | Indicates a C style void . |
VT_HRESULT | Indicates an HRESULT. |
VT_PTR | Indicates a pointer type. |
VT_SAFEARRAY | Indicates a SAFEARRAY. Not valid in a VARIANT. |
VT_CARRAY | Indicates a C style array. |
VT_USERDEFINED | Indicates a user defined type. |
VT_LPSTR | Indicates a null-terminated string. |
VT_LPWSTR | Indicates a wide string terminated by null Nothing nullptr a null reference (Nothing in Visual Basic) . |
VT_RECORD | Indicates a user defined type. |
VT_FILETIME | Indicates a FILETIME value. |
VT_BLOB | Indicates length prefixed bytes. |
VT_STREAM | Indicates that the name of a stream follows. |
VT_STORAGE | Indicates that the name of a storage follows. |
VT_STREAMED_OBJECT | Indicates that a stream contains an object. |
VT_STORED_OBJECT | Indicates that a storage contains an object. |
VT_BLOB_OBJECT | Indicates that a blob contains an object. |
VT_CF | Indicates the clipboard format. |
VT_CLSID | Indicates a class ID. |
VT_VECTOR | Indicates a simple, counted array. |
VT_ARRAY | Indicates a SAFEARRAY pointer. |
VT_BYREF | Indicates that a value is a reference. |
VARIANT使用起來(lái)是很簡(jiǎn)單的,但是有些問題,我們還必須要去注意:
SAFEARRAY的主要目的是用于automation中的數(shù)組型參數(shù)的傳遞,我們都知道,在網(wǎng)絡(luò)環(huán)境中,數(shù)組是不能直接傳遞的,
所以我們必須將數(shù)組封裝成SAFEARRAY類型,這樣才能進(jìn)行傳遞,在COM編程時(shí),SAFEARRAY類型是可以存放在VARIANT類型中,
指定vt為VT_ARRAY|*或者VT_BYREF|VT_ARRAY。對(duì)于SAFEARRAY,說白了,就是普通的數(shù)組,添加了一些額外的說明,
當(dāng)我第一次遇到這個(gè)類型時(shí),也是有點(diǎn)恐懼的,后來(lái),用慣了,也就無(wú)所謂了。SAFEARRAY單獨(dú)用的時(shí)候很少,就像我前面說的,
一般都是搭配著VARIANT一起使用,指定vt類型以后,parray成員就是指向SAFEARRAY的指針。SAFEARRAY中元素的類型可以
是VARIANT能封裝的任何類型,包括VARIANT類型本身。
訪問SAFEARRAY訪問SAFEARRAY的方法大體上有兩種:
關(guān)于這兩種方法,在上面的例子中都有涉及。
HRESULT 函數(shù)返回值
每個(gè)人在做程序設(shè)計(jì)的時(shí)候,都有他們各自的哲學(xué)思想。拿函數(shù)返回值來(lái)說,就有好多種形式。
函數(shù) | 返回值 | 返回值信息 |
double sin(double) | 浮點(diǎn)數(shù)值 | 計(jì)算正玄值 |
BOOL DeleteFile(LPCTSTR) | 布爾值 | 文件刪除是否成功。如失敗,需要GetLastError()才能取得失敗原因 |
void * malloc(size_t) | 內(nèi)存指針 | 內(nèi)存申請(qǐng),如果失敗,返回空指針 NULL |
LONG RegDeleteKey(HKEY,LPCTSTR) | 整數(shù) | 刪除注冊(cè)表項(xiàng)。0表示成功,非0失敗,同時(shí)這個(gè)值就反映了失敗的原因 |
UINT DragQueryFile(HDROP,UINT,LPTSTR,UINT) | 整數(shù) | 取得拖放文件信息。以不同的參數(shù)調(diào)用,則返回不同的含義: 一會(huì)兒表示文件個(gè)數(shù),一會(huì)兒表示文件名長(zhǎng)度,一會(huì)兒表示字符長(zhǎng)度 |
...... ...... | ... | ...... ...... |
如此紛繁復(fù)雜的返回值,如此含義多變的返回值,使得大家在學(xué)習(xí)和使用的過程中,增加了額外的困難。好了,COM 的設(shè)計(jì)規(guī)范終于對(duì)他們進(jìn)行了統(tǒng)一。組件API及接口指針中,除了IUnknown::AddRef()和IUnknown::Release()兩個(gè)函數(shù)外,其它所有的函數(shù),都以 HRESULT 作為返回值。大家想象一個(gè)組件的接口函數(shù)比如叫Add(),完成2個(gè)整數(shù)的加法運(yùn)算,在C語(yǔ)言中,我們可以如下定義:
還記得剛才我們說的原則嗎?COM 組件是運(yùn)行在分布式環(huán)境中的。也就是說,這個(gè)函數(shù)可能運(yùn)行在“地球另一邊”的計(jì)算機(jī)上,既然運(yùn)行在那么遙遠(yuǎn)的地方,就有可能出現(xiàn)服務(wù)器關(guān)機(jī)、網(wǎng)絡(luò)掉線、運(yùn)行超時(shí)、對(duì)方不在服務(wù)區(qū)......等異常。于是,這個(gè)加法函數(shù),除了需要返回運(yùn)算結(jié)果以外,還應(yīng)該返回一個(gè)值------函數(shù)是否被正常執(zhí)行了。
如果函數(shù)正常執(zhí)行,則返回 S_OK,同時(shí)真正的函數(shù)運(yùn)行結(jié)果則通過參數(shù)指針返回。如果遇到了異常情況,則COM系統(tǒng)經(jīng)過判斷,會(huì)返回相應(yīng)的錯(cuò)誤值。常見的返回值有:
HRESULT | 值 | 含義 |
S_OK | 0x00000000 | 成功 |
S_FALSE | 0x00000001 | 函數(shù)成功執(zhí)行完成,但返回時(shí)出現(xiàn)錯(cuò)誤 |
E_INVALIDARG | 0x80070057 | 參數(shù)有錯(cuò)誤 |
E_OUTOFMEMORY | 0x8007000E | 內(nèi)存申請(qǐng)錯(cuò)誤 |
E_UNEXPECTED | 0x8000FFFF | 未知的異常 |
E_NOTIMPL | 0x80004001 | 未實(shí)現(xiàn)功能 |
E_FAIL | 0x80004005 | 沒有詳細(xì)說明的錯(cuò)誤。一般需要取得 Rich Error 錯(cuò)誤信息(注1) |
E_POINTER | 0x80004003 | 無(wú)效的指針 |
E_HANDLE | 0x80070006 | 無(wú)效的句柄 |
E_ABORT | 0x80004004 | 終止操作 |
E_ACCESSDENIED | 0x80070005 | 訪問被拒絕 |
E_NOINTERFACE | 0x80004002 | 不支持接口 |
圖一、HRESULT 的結(jié)構(gòu)
HRESULT 其實(shí)是一個(gè)雙字節(jié)的值,其最高位(bit)如果是0表示成功,1表示錯(cuò)誤。具體參見 MSDN 之"Structure of COM Error Codes"說明。我們?cè)诔绦蛑腥绻枰袛喾祷刂,則可以使用比較運(yùn)算符號(hào);switch開關(guān)語(yǔ)句;也可以使用VC提供的宏:
|站長(zhǎng)郵箱|小黑屋|手機(jī)版|Office中國(guó)/Access中國(guó)
( 粵ICP備10043721號(hào)-1 )
GMT+8, 2025-7-13 03:13 , Processed in 0.070356 second(s), 17 queries .
Powered by Discuz! X3.3
© 2001-2017 Comsenz Inc.