天天看點

使用Excel COM元件導出資料後釋放 Excel程序不能正常結束

分析一下自己的錯誤:

首先用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清除就好了

繼續閱讀