VARIANT/Variant/TVariant

昨天发现系统中数据压缩解压部分有内存泄漏,上午研究了一番,发现还是实现的人对这几个东东不熟悉,现将研究结果罗列一番。

源代码如下:

 1 None.gif long  __fastcall UnVarCompress(VARIANT varRet,VARIANT  * vaDest)
 2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 3InBlock.gif    try
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 5InBlock.gif        Variant vaSource;
 6InBlock.gif        Variant vaDestTmp;
 7InBlock.gif        vaDestTmp = vaDest;
 8InBlock.gif        vaSource = varRet;
 9InBlock.gif
10InBlock.gif        ICompressionDisp xComp;
11InBlock.gif        HRESULT hr = xComp.BindDefault();
12InBlock.gif
13InBlock.gif        if (FAILED(hr))
14ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
15InBlock.gif            throw Exception("");
16ExpandedSubBlockEnd.gif        }

17InBlock.gif
18InBlock.gif        CompressionError xResult = xComp.Uncompress(vaSource, vaDestTmp, true);
19InBlock.gif
20InBlock.gif        if( xResult != xSuccess )
21InBlock.gif
22ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
23InBlock.gif            AnsiString strErr = xComp->GetErrorDescription(xResult);
24InBlock.gif            xComp.Unbind();
25InBlock.gif            throw Exception(strErr);
26ExpandedSubBlockEnd.gif        }

27InBlock.gif
28InBlock.gif        xComp.Unbind();
29InBlock.gif
30InBlock.gif        vaDest = vaDestTmp;
31ExpandedSubBlockEnd.gif    }

32InBlock.gif    catch(...)
33ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
34InBlock.gif       return -1;
35ExpandedSubBlockEnd.gif    }

36InBlock.gif    return 1;
37ExpandedBlockEnd.gif}

38 None.gif
39 None.gif long  __fastcall VARToDataSet(VARIANT varRet, AnsiString strFileName)
40 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
41InBlock.gif    long l, h;
42InBlock.gif    void *ppvdata;
43InBlock.gif    TFileStream *fm = NULL;
44InBlock.gif    VARIANT varTempRet;
45InBlock.gif
46InBlock.gif    try
47ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
48InBlock.gif        UnVarCompress(varRet,&varTempRet);
49InBlock.gif        varRet = varTempRet;
50InBlock.gif        ::SafeArrayGetLBound(varRet.parray, 1&l);
51InBlock.gif        ::SafeArrayGetUBound(varRet.parray, 1&h);
52InBlock.gif        ::SafeArrayAccessData(varRet.parray, &ppvdata);
53InBlock.gif
54InBlock.gif//采用guid做文件名,在读取后应立即删除
55InBlock.gif        fm = new TFileStream(strFileName, fmCreate);
56InBlock.gif        fm->Write(ppvdata, h - l + 1);
57InBlock.gif        ::SafeArrayUnaccessData(varRet.parray);
58ExpandedSubBlockEnd.gif    }

59InBlock.gif    __finally
60ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
61InBlock.gif        delete fm;
62InBlock.gif        fm = NULL;
63ExpandedSubBlockEnd.gif    }

64InBlock.gif
65InBlock.gif    return 0;
66ExpandedBlockEnd.gif}

67 None.gif
68 None.gif
69 None.gif接口声明为:
70 None.giftemplate  < class T >  ziplib_tlb::CompressionError __fastcall CompressionDispT < T > ::Uncompress(TVariant *  vaSource , TVariant *  vaUncompressed,   TOLEBOOL bEndOfData );

VARToDateSet是最外层,直接被其他程序员调用
而VARToDateSet内部调用UnVarCompress,UnVarCompress又调用ICompression接口进行实际解压工作

先看UnVarCompress方法
第一感觉,有2个问题
首先觉得vaDestTmp  =  vaDest这句多余
另外,vaDest  =  vaDestTmp这句将解压后的数据返回出去也有问题
因为vaDestTmp是Variant类型,在UnVarCompress执行后将被释放,那么vaDest指向的将是无效的指针
但是实际应用时这个方法一直都工作正常阿!百思不得其解,于是跟踪了一番,终于发现问题所在
在vaDestTmp  =  vaDest后,vaDestTmp实际类型变成了VT_VARIANT | VT_REF,vaDestTmp.VArray指向的是vaDest,也就是说这时候vaDestTmp指向的实际数据是另一个VARIANT vaDest
xComp.Uncompress之后vaDest.parray被正确修改,得到了实际的解压数据
反而vaDest = vaDestTmp这句是多余的......

再来看VARToDataSet方法
实际上内存泄漏是出现在这个地方的,而且有2处
第一处是varRet,这个方法中没有调用::VaraintClear清除其中的内容
有人会说了,varRet是个VARIANT结构,结构当参数传入时实际只是传了个地址而已,并不是复制了一份传进来的,因此不应该在这里释放
的确时这样,不过在我们的已有的应用中,程序员都没有做释放的工作,所以只好在这里做了
第二处是解压后的varTempRet,也没有释放

知道了问题就好办了,修改的工作其实很简单
修改后的代码如下
 1 None.gif long  __fastcall UnVarCompress(VARIANT varRet1,VARIANT  * vaDest)
 2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 3InBlock.gif    try
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 5InBlock.gif        Variant vaSource;
 6InBlock.gif        Variant vaDestTmp;
 7InBlock.gif        vaDestTmp = vaDest;
 8InBlock.gif        vaSource = varRet;
 9InBlock.gif
10InBlock.gif        IXceedCompressionDisp xComp;
11InBlock.gif        HRESULT hr = xComp.BindDefault();
12InBlock.gif
13InBlock.gif        if (FAILED(hr))
14ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
15InBlock.gif            throw Exception("");
16ExpandedSubBlockEnd.gif        }

17InBlock.gif
18InBlock.gif        CompressionError xResult = xComp.Uncompress(vaSource, vaDestTmp, true); 
19InBlock.gif
20InBlock.gif        if( xResult != xSuccess )
21InBlock.gif
22ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
23InBlock.gif            AnsiString strErr = xComp->GetErrorDescription(xResult);
24InBlock.gif            xComp.Unbind();
25InBlock.gif            throw Exception(strErr);
26ExpandedSubBlockEnd.gif        }

27InBlock.gif
28InBlock.gif        xComp.Unbind();
29ExpandedSubBlockEnd.gif    }

30InBlock.gif    catch(...)
31ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
32InBlock.gif       return -1;
33ExpandedSubBlockEnd.gif    }

34InBlock.gif    return 1;
35ExpandedBlockEnd.gif}

36 None.gif
37 None.gif long  __fastcall VARToDataSet(VARIANT varRet, AnsiString strFileName)
38 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
39InBlock.gif    long l, h;
40InBlock.gif    void *ppvdata;
41InBlock.gif    TFileStream *fm = NULL;
42InBlock.gif    VARIANT varTempRet;
43InBlock.gif
44InBlock.gif    try
45ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
46InBlock.gif        UnVarCompress(varRet,&varTempRet);
47InBlock.gif
48InBlock.gif        ::SafeArrayGetLBound(varTempRet.parray, 1&l);
49InBlock.gif        ::SafeArrayGetUBound(varTempRet.parray, 1&h);
50InBlock.gif        ::SafeArrayAccessData(varTempRet.parray, &ppvdata);
51InBlock.gif//采用guid做文件名,在读取后应立即删除
52InBlock.gif        fm = new TFileStream(strFileName, fmCreate);
53InBlock.gif        fm->Write(ppvdata, h - l + 1);
54InBlock.gif        ::SafeArrayUnaccessData(varTempRet.parray);
55ExpandedSubBlockEnd.gif    }

56InBlock.gif    __finally
57ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
58InBlock.gif        ::VariantClear(&varRet);
59
InBlock.gif        ::VariantClear(&varTempRet);
60
InBlock.gif        delete fm;
61InBlock.gif        fm = NULL;
62ExpandedSubBlockEnd.gif    }

63InBlock.gif
64InBlock.gif    return 0;
65ExpandedBlockEnd.gif}


再来说说类型转换的问题
C++Builder支持 VARIANT/Variant/TVariant
VARIANT是Windows定义的类型
Variant是BCB自己实现的类型,目的是为了兼容Delphi的Variant
TVariant则是对VARIANT的封装

用代码说话
Variant v;
VARIANT wv, *pwv;
TVariant tv, *ptv;
wv = v; pwv = v;
tv = v; ptv = v;
看看地址
&v: :0012F364
v.VArray: :00156DD8
&tv: :0012F354
tv.parray: :00156B10
ptv: :0012F364
ptv->parray: :00156DD8
&wv: :0012F2F0
wv.parray: :00156E18
pwv: :0012F364
pwv->parray: :00156DD8
看出什么问题?对,&v,ptv,pwv指向的都是v!而tv,wv则是对v的拷贝

比较有意思的是
VARIANT wv...
Varaint v1 = &wv;
Variant v2; v2 = &wv;
此时 v1.VArray指向的是 &wv,也就是说类型为 VT_VARIANT | VT_REF,而v2中VArray指向的是一份wv的内容的拷贝,类型与wv类型一致
产生这样的结果原因跟编译器有关
Varaint v1 = &wv实际跟Variant v1(&wv)是一样的,也就是说调用的是Variant的构造
Variant v2; v2 = &wv;实际是先构造v2,然后将&wv赋值给v2,调用的是v2重载的操作符=

转载于:https://www.cnblogs.com/sephil/archive/2006/12/07/VARIANT.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值