1. 程式人生 > >COM 中的數據類型

COM 中的數據類型

big all 區分 top .... exp clas 自己的 tchar

  1.HRESULT 函數返回值

  組件API及接口指針中,除了IUnknown::AddRef()和IUnknown::Release()兩個函數外,其它所有的函數,都以 HRESULT作為返回值。

  想象一個組件的接口函數比如叫Add(),完成2個整數的加法運算,在C語言中,我們可以如下定義:

 long Add( long n1, long n2 )
      {
          return n1 + n2;
      }

  COM 組件是運行在分布式環境中的。也就是說,這個函數可能運行在“地球另一邊”的計算機上,既然運行在那麽遙遠的地方,就有可能出現服務器關機、網絡掉線、運行超時、對方不在服務區......等異常。於是,這個加法函數,除了需要返回運算結果以外,還應該返回一個值------函數是否被正常執行了。

HRESULT Add( long n1, long n2, long *pSum )
      {
          *pSum = n1 + n2;
          return S_OK;
      }

  如果函數正常執行,則返回 S_OK,同時真正的函數運行結果則通過參數指針返回。如果遇到了異常情況,則COM系統經過判斷,會返回相應的錯誤值。常見的返回值有:

HRESULT 含義
S_OK 0x00000000 成功
S_FALSE 0x00000001 函數成功執行完成,但返回時出現錯誤
E_INVALIDARG 0x80070057 參數有錯誤
E_OUTOFMEMORY 0x8007000E
內存申請錯誤
E_UNEXPECTED 0x8000FFFF 未知的異常
E_NOTIMPL 0x80004001 未實現功能
E_FAIL 0x80004005 沒有詳細說明的錯誤。
E_POINTER 0x80004003 無效的指針
E_HANDLE 0x80070006 無效的句柄
E_ABORT 0x80004004 終止操作
E_ACCESSDENIED 0x80070005 訪問被拒絕
E_NOINTERFACE 0x80004002 不支持接口

  HRESULT 其實是一個雙字節的值,其最高位(bit)如果是0表示成功,1表示錯誤:

技術分享

  在程序中如果需要判斷返回值,可以采用SUCCEEDED宏和FAILED宏:

 HRESULT hr = 調用組件函數;
      
if( SUCCEEDED( hr ) ){...} // 如果成功 ...... if( FAILED( hr ) ){...} // 如果失敗 ......

2.UNICODE   

  計算機發明後,為了在計算機中表示字符,人們制定了一種編碼,叫ASCII碼。ASCII碼由一個字節中的7位(bit)表示,範圍是0x00 - 0x7F 共128個字符。他們以為這128個數字就足夠表示abcd....ABCD....1234 這些字符了。後來發現,如果需要按照表格方式打印這些字符的時候,缺少了“制表符”。於是又擴展了ASCII的定義,使用一個字節的全部8位(bit)來表示字符了,這就叫擴展ASCII碼。範圍是0x00 - 0xFF 共256個字符。

  中國人利用連續2個擴展ASCII碼的擴展區域(0xA0以後)來表示一個漢字,該方法的標準叫GB-2312。後來,日文、韓文、阿拉伯文、臺灣繁體(BIG-5)......都使用類似的方法擴展了本地字符集的定義,現在統一稱為 MBCS 字符集(多字節字符集)。這個方法是有缺陷的,因為各個國家地區定義的字符集有交集,因此使用GB-2312的軟件,就不能在BIG-5的環境下運行(顯示亂碼),反之亦然。   

  為了把全世界人民所有的所有的文字符號都統一進行編碼,於是制定了UNICODE標準字符集。UNICODE 使用2個字節表示一個字符(unsigned shor int、WCHAR、_wchar_t、OLECHAR)。這下終於好啦,全世界任何一個地區的軟件,可以不用修改地就能在另一個地區運行了。雖然我用 IE 瀏覽日本網站,顯示出我不認識的日文文字,但至少不會是亂碼了。UNICODE 的範圍是 0x0000 - 0xFFFF 共6萬多個字符,其中光漢字就占用了4萬多個。嘿嘿,中國人賺大發了。

  在程序中使用各種字符集的方法:

const char * p = "Hello"; // 使用 ASCII 字符集
      const char * p = "你好"; // 使用 MBCS 字符集,由於 MBCS 完全兼容 ASCII,多數情況下,我們並不嚴格區分他們
      LPCSTR p = "Hello,你好"; // 意義同上
      
      const WCHAR * p = L"Hello,你好"; // 使用 UNICODE 字符集
      LPCOLESTR p = L"Hello,你好"; // 意義同上
      
      // 如果預定義了_UNICODE,則表示使用UNICODE字符集;如果定義了_MBCS,則表示使用 MBCS
      const TCHAR * p = _T("Hello,你好"); 
      LPCTSTR p = _T("Hello,你好"); // 意義同上

  在上面的例子中,T是非常有意思的一個符號,它表示使用一種中間類型,既不明確表示使用 MBCS,也不明確表示使用 UNICODE。那到底使用哪種字符集?編譯的時候決定吧。使用 T 類型,是非常好的習慣,嚴重推薦!
3.BSTR
  COM 中除了使用一些簡單標準的數據類型外,字符串類型需要特別重點地說明一下。COM組件是運行在分布式環境中的。通俗地說,你不能直接把一個內存指針直接作為參數傳遞給COM函數。你想想,系統需要把這塊內存的內容傳遞到“地球另一 邊”的計算機上,因此,我至少需要知道你這塊內存的尺寸吧?不然讓我如何傳遞呀?傳遞多少字節呀?!而字符串又是非常常用的一種類型,因此 COM 設計者引入了 BASIC 中字符串類型的表示方式---BSTR。BSTR 其實是一個指針類型,它的內存結構是:(輸入程序片段 BSTR p =::SysAllocString(L"Hello,你好");斷點執行,然後觀察p的內存)

技術分享

  BSTR 是一個指向 UNICODE 字符串的指針,且 BSTR 向前的4個字節中,使用DWORD保存著這個字符串的字節長度(沒有含字符串的結束符)。因此系統就能夠正確處理並傳送這個字符串到“地球另一 邊”了。特別需要註意的是,由於BSTR的指針就是指向 UNICODE 串,因此
  BSTR 和 LPOLESTR 可以在一定程度上混用,但一定要註意:
  有函數 fun(LPCOLESTR lp),則你調用 BSTR p=...; fun(p); 正確
  有函數 fun(const BSTR bstr),則你調用 LPCOLESTR p=...; fun(p); 錯誤!!!

  有關 BSTR 的處理函數:

API 函數 說明
SysAllocString() 申請一個 BSTR 指針,並初始化為一個字符串
SysFreeString() 釋放 BSTR 內存
SysAllocStringLen() 申請一個指定字符長度的 BSTR 指針,並初始化為一個字符串
SysAllocStringByteLen() 申請一個指定字節長度的 BSTR 指針,並初始化為一個字符串
SysReAllocStringLen() 重新申請 BSTR 指針

CString 函數

說明

AllocSysString() 從 CString 得到 BSTR
SetSysString() 重新申請 BSTR 指針,並復制到 CString 中

CComBSTR 函數

ATL 的 BSTR 包裝類。在 atlbase.h 中定義

Append()、AppendBSTR()、AppendBytes()、ArrayToBSTR()、BSTRToArray()、AssignBSTR()、Attach()、Detach()、Copy()、CopyTo()、Empty()、Length()、ByteLength()、ReadFromStream()、WriteToStream()、LoadString()、ToLower()、ToUpper() 運算符重載:!,!=,==,<,>,&,+=,+,=,BSTR 太多了,但從函數名稱不能看出其基本功能。詳細資料,查看MSDN 吧。另外,左側函數,有很多是 ATL 7.0 提供的,VC6.0 下所帶的 ATL 3.0 不支持。 由於我們將來主要用 ATL 開發組件程序,因此使用 ATL 的 CComBSTR 為主。VC也提供了其它的包裝類 _bstr_t。

  各種字符串類型之間的轉換:

  函數 WideCharToMultiByte(),轉換 UNICODE 到 MBCS。

  函數 MultiByteToWideChar(),轉換 MBCS 到 UNICODE。

  使用 ATL 提供的轉換宏:

A2BSTR OLE2A T2A W2A
A2COLE OLE2BSTR T2BSTR W2BSTR
A2CT OLE2CA T2CA W2CA
A2CW OLE2CT T2COLE W2COLE
A2OLE OLE2CW T2CW W2CT
A2T OLE2T T2OLE W2OLE
A2W OLE2W T2W W2T

上表中的宏函數,其實非常容易記憶:

2 好搞笑的縮寫,to 的發音和 2 一樣,所以借用來表示“轉換為、轉換到”的含義。
A ANSI 字符串,也就是 MBCS。
W、OLE 寬字符串,也就是 UNICODE。
T 中間類型T。如果定義了 _UNICODE,則T表示W;如果定義了 _MBCS,則T表示A
C const 的縮寫

使用 ATL 轉換宏,由於不用釋放臨時空間,所以使用起來非常方便。但是考慮到棧空間的尺寸(VC 默認2M),使用時要註意幾點:
1)只適合於進行短字符串的轉換;

2)不要試圖在一個次數比較多的循環體內進行轉換;

3)不要試圖對字符型文件內容進行轉換,因為文件尺寸一般情況下是比較大的;

4)對情況 2 和 3,要使用 MultiByteToWideChar() 和 WideCharToMultiByte();

4.VARIANT
  C++、BASIC、Java、Pascal、Script......計算機語言多種多樣,而它們各自又都有自己的數據類型,COM產生目的,其中之一就是要跨語言(註3)。而 VARIANT 數據類型就具有跨語言的特性,同時它可以表示(存儲)任意類型的數據。從C語言的角度來講,VARIANT其實是一個結構,結構中用一個域(vt)表示------該變量到底表示的是什麽類型數據,同時真正的數據則存貯在 union空間中。結構的定義太長了(雖然長,但其實很簡單)大家去看 MSDN 的描述吧。
  VARIANT有一個變量vt, 類型為VARTYPE,指定了數據類型:比如:VT_I2,VT_I4,VT_R4,VT_R8等,分別表示short,long,float,double,對應的值分別存在iVal,lVal,fltVal,dblVal中。

  註意表示bool值時:

  要寫成:VARIANT v; v.vt=VT_BOOL; v.boolVal=VARIANT_TRUE;

  不能寫成:v.boolVal=true;

類型 字節長度 假值 真值
bool 1(char) 0(false) 1(true)
BOOL 4(int) 0(FALSE) 1(TRUE)
VT_BOOL 2(short int) 0(VARIANT_FALSE) -1(VARIANT_TRUE)

  v.boolVal=true 這樣賦值,那麽將來 if(VARIANT_TRUE==v.boolVal) 的時候會出問題(-1 != 1)。但是你註意觀察,任何布爾類型的“假”都是0,因此作為一個好習慣,在做布爾判斷的時候,不要和“真值”相比較,而要與“假值”做比較。

  VARIANT 保存字符串:VARIANT v; v.vt=VT_BSTR; v.bstrVal=SysAllocString(L"Hello,你好");

(1)VARIANT的初始化

VARIANT va;

va.vt=VT_I4;

va.lVal=5;

或者:

VARIANT va;

VariantInit(&va);

(2)VARIANT的拷貝 VariantCopy

VARIANT va;

VARIANT va2;

va.vt=VT_I4;

va.lVal=5;

VariantCopy(&va2,&va);

這樣就將 va的內容,拷貝到va2了。它會根據不同的數據類型,執行深拷貝或者淺拷貝。

(3)VARIANT的銷毀 VariantClear

VariantClear(&va);

COM 中的數據類型