DOBON.NET DOBON.NETプログラミング掲示板過去ログ

ヒープが壊れるエラー

環境/言語:[VisualStudiio2005 C++ MFC]
分類:[.NET]

こんにちは、現在MFCでOLEのドラッグ&ドロップの動作を作成しているのですが、
文字列をドラッグしてテキストエディターにドロップすると以下のエラーが出てきてしまいます。


【エラー内容】
ヒープが壊れていることが原因として考えられます。XXX.exeまたは読み込まれたDLLにバグがあります。


ドラッグしている文字列はきちんと取れているように思います。
何か解決する方法はないでしょうか。




【ドラッグ側のソース(リストコントロールのイベントにしています)】
void TestDlg::OnLvnBegindragList(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    // TODO: ここにコントロール通知ハンドラ コードを追加します。
    HGLOBAL hGlobal;
            //  DnDするデータを作成
    hGlobal = "ABC";
    COleDataSource dataSource;                      //  データソースオブジェクトをインスタンス化
    dataSource.CacheGlobalData(CF_TEXT, hGlobal);   //  作成したデータを設定
    DROPEFFECT result = dataSource.DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE);
    if( result == DROPEFFECT_MOVE ) {
        delete hGlobal;
    }
    *pResult = 0;
}



【エラーの出る箇所(1)(olemisc.cpp)】
テキストエディタにドロップした場合

AFX_STATIC HGLOBAL AFXAPI _AfxCopyGlobalMemory(HGLOBAL hDest, HGLOBAL hSource)
{
    ASSERT(hSource != NULL);

    // make sure we have suitable hDest
    ULONG_PTR nSize = ::GlobalSize(hSource);←●ここを実行する際にエラー発生
    if (hDest == NULL)
    {
        hDest = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, nSize);
        if (hDest == NULL)
            return NULL;
    }
//-----------------------以下省略--------------------------




【エラーの出る個所(2)(oledobj2.cpp)】
ダイアログ内の何もない個所でドロップした場合

void COleDataSource::Empty()
{
    if (m_pDataCache != NULL)
    {
        ASSERT(m_nMaxSize != 0);
        ASSERT(m_nSize != 0);

        // release all of the STGMEDIUMs and FORMATETCs
        for (UINT nIndex = 0; nIndex < m_nSize; nIndex++)
        {
            CoTaskMemFree(m_pDataCache[nIndex].m_formatEtc.ptd);
            ::ReleaseStgMedium(&m_pDataCache[nIndex].m_stgMedium);←●ここを実行の際にエラー
        }

//-----------------------以下省略--------------------------
■No28420に返信(kintonyさんの記事)
> ドラッグしている文字列はきちんと取れているように思います。
> 何か解決する方法はないでしょうか。

どうしてそのように判断したのでしょうか?

> hGlobal = "ABC";

間違ってもこんな代入文を書いてはいけません。

HGLOBAL は GlobalAlloc を使って確保するものです。
それに対して変なポインタを与えているので、それを GlobalSize を与えたので不正なポインタ操作になっていると考えられます。

クリップボードとか、OLE D&D とかのサンプルではきちんと GlobalAlloc を使ったコードがいくつか見つかると思いますので、もう一度参考にし直してください。
ありがとうございます。

> どうしてそのように判断したのでしょうか?
ドロップ側には文字列データが渡っていました。

> HGLOBAL は GlobalAlloc を使って確保するものです。
> それに対して変なポインタを与えているので、それを GlobalSize を与えたので不正なポインタ操作になっていると考えられます。

以下のように修正しました所、
・テキストエディタにドロップした場合
・ダイアログ内の何もない個所でドロップした場合

では、エラーが出ず、うまくドロップされたデータを渡すことができるのですが、
C#で作成したフォームにドラッグしてデータがわたるか確認したところ、
データを取得した後に、エラーが出てしまいます。

【エラー内容】
ヒープが壊れていることが原因として考えられます。XXX.exeまたは読み込まれたDLLにバグがあります。

【エラーの出る関数(dbgheat.c)】
extern "C" _CRTIMP int __cdecl _CrtIsValidHeapPointer(
        const void * pUserData
        )

VCダイアログからC#のフォームにドラッグアンドドロップすると、データ受け取り後にエラーが出ますが、
C#フォームからVCダイアログにドラッグアンドドロップすると上手くいきます。

【修正したドラッグ側のソース】
void TestDlg::OnLvnBegindragList(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    // TODO: ここにコントロール通知ハンドラ コードを追加します。

    HGLOBAL hGlobal;
    hGlobal = GlobalAlloc(GHND,10);                //  DnDするデータ領域を作成
    char* ptr= (char*)::GlobalLock(hGlobal);
    strcpy(ptr, "ABC");
    ::GlobalUnlock(hGlobal);

    COleDataSource dataSource;                      //  データソースオブジェクトをインスタンス化
    dataSource.Empty();
    dataSource.CacheGlobalData(CF_TEXT, hGlobal);   //  作成したデータを設定

    DROPEFFECT result = dataSource.DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE);

    if( result == DROPEFFECT_MOVE ) {
        delete hGlobal;
    }

    *pResult = 0;
}
今出ている問題の原因かは実行したり、きちんと分析していないので不詳ですが、一点指摘をしておきます。

■No28428に返信(kintonyさんの記事)
> delete hGlobal;

なぜ、このようなコードを書いているのでしょうか?
GlobalAlloc の説明ページを読んでいますか?
> なぜ、このようなコードを書いているのでしょうか?
> GlobalAlloc の説明ページを読んでいますか?

すみません、よく見たら書いてありましたね。
delete hGlobal;から
::GlobalFree(hGlobal);
に直しました。

今までのエラーはでなくなったのですが、
VCからC#フォームへドラッグドロップを実行すると、以下のエラーが
出てきました。

Debug Assertion Failed!

Program: ...
File: f:\dd/vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp
Line: 43

For information on how your program can cause an assertion failure, see the Visual C++ documentation on asserts.

これは、GlobalFreeのMSDNの説明ページにある文↓のことなのでしょうか?
リリースで実行すると出てきません。

 アプリケーションがデバッグ版の環境で動作している場合、GlobalFree は、ロック済みのオブジェクトを解放しようとするときに、そのことを知らせるメッセージを発行します。
 アプリケーションをデバッグしている場合、GlobalFree はロック済みオブジェクトを解放する直前のブレークポイントに入ります。
 この結果、この動作が意図したものであるかどうかを確認し、実行を続けることができます。
自己解決いたしました。

http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+201011/10110009.txt

こちらを参考にして
   COleDataSource* dataSource = new COleDataSource();
      dataSource->DoDragDrop();
      dataSource->InternalRelease();

と、DoDragDrop()後に、InternalRelease()したところエラーが出なくなりました。

ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板