鍵字: COM的數(shù)據(jù)類型BSTR,Variant
COM的特性是語言中立、硬件結(jié)構(gòu)中立,很明顯,它需要一個(gè)語言中立、硬件結(jié)構(gòu)中立的文本數(shù)據(jù)類型。
OLECHAR和BSTR就是干這個(gè)用的。
OLECHAR:在編譯源代碼的目標(biāo)操作系統(tǒng)上COM使用的字符類型。
對于Win32操作系統(tǒng),這是wchar_t字符類型。
對于Win16操作系統(tǒng),這是char字符類型。
對于MacOS,這是char類型。
對于Solaris OS,這是wchar_t字符類型。
對于其他未知OS,沒人知道是什么類型。
OLECHAR是抽象數(shù)據(jù)類型。COM使用它,不管實(shí)際映射到什么特定的底層數(shù)據(jù)類型。
很多語言,比如vbScript和JScript使用BSTR(出于性能考慮)。
BSTR是一個(gè)指向包含長度前綴的OLECHAR字符數(shù)組的指針。
BSTR是一個(gè)指針數(shù)據(jù)類型,指向數(shù)組的第一個(gè)字符,
長度前綴以整數(shù)的形式恰好存儲(chǔ)在數(shù)組的第一個(gè)字符之前。
BSTR字符數(shù)組以NUL字符作為結(jié)尾,
長度前綴以字節(jié)為單位而不是字符,并且不包括NUL終止符。
字符數(shù)組內(nèi)部可以包含嵌入的NUL字符。
必須使用SysAllocString和SysFreeString函數(shù)族來分配和釋放。
Null BSTR指針意味著一個(gè)空字符串。
BSTR沒有引用計(jì)數(shù),因此相同字符串內(nèi)容的兩個(gè)引用必須指向不同的BSTR。
換句話說,復(fù)制BSTR意味著制作字符串的一個(gè)拷貝,而不是簡單地復(fù)制指針。
調(diào)用SysStringLen時(shí),必須BSTR非Null,也就是說,如果要獲得某個(gè)bstr的長度,需要這樣:
UINT uLen = bstr? SysStringLen(bstr) : 0;
VBScript和JScript內(nèi)部都使用BSTR來表示字符串。
總之BSTR是包含長度前綴的OLECHAR數(shù)組。
雖然wtypes.h里面,這樣定義BSTR:typedef /* [wire_marshal] */ OLECHAR __RPC_FAR *BSTR
但是,BSTR并不是OLECHAR *,能使用OLECHAR*的地方,并不一定都能夠使用BSTR。
這可是很麻煩的事情,編譯器認(rèn)為他們一樣,但他們又不一樣,很容易出錯(cuò),
問題就在于BSTR第一個(gè)字符是長度前綴,而OLECHAR*不是。
比如:STDMETHODIMP CMyClass::put_Name(LPCOLESTR pName);
BSTR bstrIn = ...
pObj->put_Name(bstrIn);
由于put_Name是一個(gè)LPCOLESTR,它有可能截?cái)郻strIn(如果bstrIn可以有內(nèi)嵌的Null字符)。
再比如,需要[out]OLECHAR*參數(shù)的地方,也不能使用BSTR,否則可能會(huì)造成內(nèi)存泄漏。
STDMETHODIMP CMyClass::get_Name(/* [out] */ OLECHAR ** ppName)
{
BSTR bstrOut = ...
*ppName = bstrOut;
return S_OK;
}
上面的代碼中,調(diào)用者無法釋放BSTR,所以就造成了內(nèi)存泄漏。
同樣,需要BSTR時(shí),用OLECHAR *也會(huì)存在錯(cuò)誤。
STDMETHODIMP CMyClasss::put_Name(BSTR bstrName);
pObj->put_Name(OLECHAR("This is a Ole Char!"));
put_Name會(huì)調(diào)用SysStringLen來獲得BSTR的長度,
當(dāng)試圖從字符串前面的整數(shù)獲取BSTR的長度時(shí),就會(huì)出現(xiàn)問題。
COM的封裝類CComBSTR的構(gòu)造函數(shù)有CComBSTR(const OLECHAR*),
要特別注意不要在這個(gè)構(gòu)造函數(shù)中用到BSTR,
如果確實(shí)需要,最好指定長度,或者先Empty(),然后再AppendBSTR()
CComBSTR
CComBSTR是一個(gè)ATL工具類,它是對BSTR的一個(gè)有效封裝,頭文件在atlbase.h中。
它有一個(gè)數(shù)據(jù)成員BSTR m_str;還有n多的成員函數(shù),
這些函數(shù)的實(shí)現(xiàn),也都可以查看,具體實(shí)現(xiàn),用了不少技巧,但并不復(fù)雜。
在看這些過程中,我對SysAllocStringLen,SysAllocString,SysFreeString有了更深的認(rèn)識。
Length函數(shù)要注意,由于SysStringLen(NULL)會(huì)出錯(cuò)(我的xp + vc60沒有出錯(cuò)),
所以要這樣寫:
return m_str == NULL ? 0 : SysStringLen(m_str)
CComVariant
CComVariant從tagVARIANT(也就是VARIANT)繼承而來。
使用COM的時(shí)候,希望在對方法所要求的數(shù)據(jù)類型一無所知的情況下,向它傳遞參數(shù)。
為了使方法能夠解釋接收到的參數(shù),調(diào)用者必須指定數(shù)據(jù)的格式和相應(yīng)的值。
格式存放在vt中,值存放在聯(lián)合體變量中,具體的對應(yīng)關(guān)系如下:
VT_EMPTY 沒有指定值,如果可缺省的參數(shù)要放空,不能指定為VT_EMPTY,而是VT_ERROR,值為DISP_E_PARAMNOTFOUND
VT_EMPTY | VT_BYREF 不合法
VT_UI1 一個(gè)無符號一個(gè)字節(jié)長的字符,存儲(chǔ)在bVal
VT_UI1 | VT_BYREF 一個(gè)無符號一個(gè)字節(jié)長的字符引用,存儲(chǔ)在pbVal
其他的也都大同小異,不一一列舉,單說值得注意的。
VT_NULL 傳遞空值,用于Sql中。
VT_NULL | VT_BYREF 不合法
VT_VARIANT 不合法,如果是VARIANT只能是引用。
VT_VARIANT | VT_BYREF 不能引用已經(jīng)是VT_VARIANT | VT_BYREF的數(shù)據(jù)。
VT_ARRAY | <type> 傳遞type類型的數(shù)組,存儲(chǔ)在pparray,type不能是VT_EMPTY和VT_NULL
CComBSTR和ComVariant是BSTR和VARIANT的封裝,封裝的還是很巧妙,
我深刻感覺的了看別人代碼的重要性,很多技巧,都是模擬、剽竊來的。
Atach和Detach方法避免了額外的內(nèi)存分配和Addref/Release調(diào)用。
但要注意,不要隨意用Detach一個(gè)[out]參數(shù),因?yàn)镈etach時(shí),要先清除參數(shù),
而out參數(shù)在進(jìn)入方法時(shí),沒有初始化,清除沒有初始化的隨機(jī)信息是非常危險(xiǎn)的。
要徹底理解他們,就要非常熟悉BSTR和VARIANT。
詳細(xì)出處參考:http://www.itqun.net/content-detail/188530_2.html
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點(diǎn)擊舉報(bào)。