51817


アプリケーションからページを出力する際にメモリが上がっているので調べたところ、  
GetCellData関数の  
if(formula) data = range.GetFormula();  
else data = range.GetValue(vtMissing);  
で上がるのを確認しました。  
ですが初心者なので、どう直すか悩んでおります。  
メモリ使用量を減らすには、どうしたらよいでしょうか。  
内容につきましてはマルチポストです。  
<https://teratail.com/questions/165797>

GetCellData関数1

[code]

    void CExcelCtrl::GetCellData(int cols,int rows,int cole,int rowe,CStringArray &dt,bool formula){
    CString sc,ec;
    sc.Format("%s%d",Num2Col(cols),rows);
    ec.Format("%s%d",Num2Col(cole),rowe);
    GetCellData(sc,ec,dt,formula);}
    
[/code]

GetCellData関数2

[code]

    void CExcelCtrl::GetCellData(LPCSTR sc,LPCSTR ec,CStringArray &dt,bool formula){
    try{
        //_Application  m_excel;
        _Worksheet ws = m_excel.GetActiveSheet();
        Range range = ws.GetRange(COleVariant(sc),COleVariant(ec));
    
        COleVariant data;
        if(formula) data = range.GetFormula();
        else        data = range.GetValue(vtMissing);
    
        COleSafeArray sa;
        sa.Attach(data);
    
        long rowmax,colmax;
        sa.GetUBound(1,&rowmax);
        sa.GetUBound(2,&colmax);
    
        CString str;
        VARIANT val;
        long idx[2];
    
        dt.RemoveAll();
        for(long row = 1;row <= rowmax;row++){
            idx[0] = row;
            for(long col = 1;col <= colmax;col++){
                idx[1] = col;
                sa.GetElement(idx,&val);
                switch(val.vt){
                case    VT_R8:
                    str.Format("%1.2f", val.dblVal);
                    break;
                case    VT_BSTR:
                    str.Format("%s",(CString)val.bstrVal);
                    ::SysFreeString(val.bstrVal);
                    break;
                case    VT_EMPTY:
                    str.Empty();
                    break;
                }
                dt.Add(str);
            }
        }
    }catch(COleDispatchException *e){
        AfxMessageBox(e->m_strDescription,MB_ICONEXCLAMATION);
        dt.RemoveAll();
    }
    
[/code]


CC BY-SA 4.0
2019-01-08T01:37:11.957
51810
2019-01-18T08:36:57.683
2019-01-18T08:36:57.683
31708
31708
question
c++ mfc
GetCellData()でメモリの使用量が上がる


<https://teratail.com/questions/165797> で解決済みとのことですが、あの回答は間違っています。なお、
[マルチポスト](https://ja.meta.stackoverflow.com/q/2418/4236)と呼ばれ、両サイトの閲覧者・回答者に対して不誠実な行動です。

`VARIANT val;` は生の[`VARIANT`構造体](https://docs.microsoft.com/en-
us/windows/desktop/api/oaidl/ns-oaidl-
tagvariant)を使用しているため、変数`val`に対しては`VariantInit()`や`VariantClear()`の呼び出しが必要です。これらを暗黙的に呼び出す[`COleVariant`クラス](https://docs.microsoft.com/en-
us/cpp/mfc/reference/colevariant-
class?view=vs-2017)に切り替えるといいでしょう。その場合、`VariantInit()`や`VariantClear()`が不要になるほか`SysFreeString()`は呼び出してはいけません。

[code]

    COleVariant val;
    
[/code]

* * *

> これってdataへの代入が悪かったということなのでしょうか。

見逃していました。おっしゃる通り、代入に問題があります。`COleVariant`は設計が古く、使い方を誤ると適切に管理できません。

>
[code]

>     COleVariant data;
>     if(formula) data = range.GetFormula();
>     else        data = range.GetValue(vtMissing);
>  
[/code]

この部分で、`data`への代入で`VARIANT`の新たなコピーが作成されます。その上で右辺値である`Range::GetFormula()`や`Range::GetValue()`の値は、何も処理されないまま捨てられます。これによってメモリ解放漏れとなります。正しくは[`COleVariant::Attach()`](https://docs.microsoft.com/ja-
jp/cpp/mfc/reference/colevariant-
class?view=vs-2017#attach)を使用することでコピーせず管理を引き継ぐ必要があります。

[code]

    COleVariant data;
    if(formula) data.Attach(range.GetFormula());
    else        data.Attach(range.GetValue(vtMissing));
    
[/code]

「設計が古い」と指摘しましたが、C++11では[ムーブセマンティクス](https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html)という概念が導入されています。これに対応していれば、`=`の代入によって値が解放漏れするようなミスを防ぐことができます。が、`COleVariant`（に限らずMFC全体）はこれに対応していないため漏れます。


CC BY-SA 4.0
2019-01-08T05:17:11.867
51817
2019-01-11T03:32:55.657
2019-01-11T03:32:55.657
4236
4236
51810
answer