分析一下自己的錯誤:
首先用Range的GetItem取到的是一個VARIANT,内含IDispatch接口,我一直以為内含的是一個BSTR,是以我已一開始直接用
_bstr_t bs(rg.GetItem(_variant_t((short)1),_variant_t((short)2)));
來擷取字元串(主要是的确能獲得字元串),根據lop5712(LOP)的提醒,發現傳回的VARIANT是接口并不是BSTR;
看來_bstr_t這個類可以把IDispatch接口直接轉換為字元串。
跟蹤代碼發現,在給一個_bstr_t或COleVariant指派VARIANT類型的時候,如果VARIANT不是VT_BSTR,這時候_bstr_t會調用OLE函數VariantChangeType進行類型轉換,根據MSDN說明,如果轉換是從一個IDispatch到BSTR,這時VariantChangeType會嘗試接口的Value屬性,而rg.GetItem傳回的接口的确存在Value屬性,因而_bstr_t和COleVariant能取到值。
根據上述原因,我們應該釋放VARIANT所含的接口,但我用#import 指令導入Excel庫的時候,卻沒有碰到這個問題,看來兩種方式的代碼是有差別的。
首先談一下#import導入的情況:
Range 的GetItem聲明為:
inline _variant_t Excel::Range::GetItem ( const _variant_t & RowIndex, const _variant_t & ColumnIndex ) {
VARIANT _result;
_com_dispatch_method(this, 0xaa, DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&_result,
L "/x000c/x080c ", &RowIndex, &ColumnIndex);
return _variant_t(_result, false);
}
這種情況下傳回的是_variant_t(_result, false);
也就是說已經對傳回的VARIANT用_variant_t包裝了,而且重要的是第二個參數,第二個參數false告訴_variant_t包裝VARIANT時僅僅把内容完整的複制過來,如果不含這個false(預設為true),_variant_t将采用OLE函數VariantCopy,根據MSDN,VariantCopy在碰到BSTR時會再配置設定一個空間,碰到接口類型時将對接口添加一個引用,是以_variant_t(_result, false)直接把傳回值存起來,并在析構的時候自動釋放内含的IDispatch。
再來看一下用MFC導入的情況:
聲明如下:
VARIANT Range::GetItem(const VARIANT& RowIndex, const VARIANT& ColumnIndex)
這時候傳回的是未經過包裝的VARIANT,不會自動釋放,需要手動完成。
如果用_bstr_t bs(rg.GetItem(_variant_t((short)1),_variant_t((short)2)));
将丢失傳回的VARIANT,這就是我發生的錯誤。
我看到有的網站上的例子是這樣的:
_variant_t vt(rg.GetItem(_variant_t((short)1),_variant_t((short)2)));
或
_variant_t vt=rg.GetItem(_variant_t((short)1),_variant_t((short)2));
這樣做其實也是有問題的,雖然取到了VARIANT的值,但是不帶false的_variant_t會添加一個引用,析構_variant_t隻會Release一次,仍然造成對象釋放不完全。
解決辦法:每次VARIANT中的内容取出來後,調用VariantClear() 将VARIANT清除就好了