MFC OLE ドラッグ&ドロップでアサートエラー

[戻る]
デゴルガン 2010/11/07(日) 15:59:51
Windows Vista、Microsoft Visual Studio 2005 を使用しています。

MFCの雛形を作成し、ウィンドウクラスにOnLButtonDownメッセーハンドラを追加。
以下のコードを記述しました。

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼
び出します。
    COleDataSource oleDataSource;

    HGLOBAL hMem = ::GlobalAlloc(GHND, 10);
    WCHAR* pData = (WCHAR*)::GlobalLock(hMem);
    wcscpy(pData, L"テキスト");
    ::GlobalUnlock(hMem);
    oleDataSource.CacheGlobalData(CF_UNICODETEXT, hMem);

    DROPEFFECT dropEffect = oleDataSource.DoDragDrop(DROPEFFECT_COPY);

    CWnd::OnLButtonDown(nFlags, point);
}

COleDataSourceにUnicodeテキストをセットし、DoDragDropを呼び出しています。

テキストのドロップを受け取れるVisual Studioのコードエディタや、
多くのフリーのテキストエディタには正常にドロップできます。

ただ、Google Chrome のアドレスバーにドロップすると、
直後にMFC内部でアサートエラーが発生します。

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

ASSERT(m_dwRef <= 1)

この時のm_dwRefの値は4になっていました。

また、フリーのペイントソフト Paint.NET では、ドラッグ中にウィンドウ上を通過する
だけで、
適当な場所で(どこでも)マウスボタンを離すと同様のアサートエラーが発生します。

ドロップデータをANSI文字に変えても駄目でした。
また、Paint.NET の場合、ドロップデータに何もセットしなくてもエラーが発生しま
す。

現在確認している不具合の出るソフトは、Google Chrome、Paint.NET のみです。
これらのアプリケーションのバグなのでしょうか?
または、上記のコードに間違いがあるのでしょうか?

よろしくお願いします。

仲澤@失業者 2010/11/08(月) 13:04:01
関係ないかも知れませんが、一応注意喚起しておくと、
DoDragDrop()はそのモードに入った後の処理の「結果」を戻します。
その結果により動作が異なるはずなのですが、その辺のコードは
大丈夫ですか。結果コードには

DROPEFFECT_NONE   ドロップできません。
DROPEFFECT_COPY   コピー操作が実行されます。
DROPEFFECT_MOVE   移動操作が実行されます。
DROPEFFECT_LINK   ドロップされたデータと元のデータがリンクされます。
DROPEFFECT_SCROLL ターゲットでドラッグ スクロール操作が実行されようとしてい
         るか、または進行中です。

がありますが、特にDROPEFFECT_NONEの場合は確保したメモリーを
開放する必要があると思います。単にアップ時に省略しただけなら良いですけど。

次に、派生元の処理をDoDragDrop()の後にやっていますが、支障がなければ、
前にやっといたほうが、安全だと考えます。

仲澤@失業者 2010/11/08(月) 13:05:44
追加。
    CWnd::OnLButtonDown(nFlags, point);
は
    CChildView::OnLButtonDown(nFlags, point);
の間違いのはず。まぁ、結果的に同等かもしれませんけど(vv;)。

仲澤@失業者 2010/11/08(月) 13:10:34
間違った。orz
  CChildViewの派生元::OnLButtonDown(nFlags, point);
たぶん
  CView::OnLButtonDown(nFlags, point);

ではないでしょうか。間違ってたらごめんなさい。

デゴルガン 2010/11/08(月) 20:29:15
回答ありがとうございます。

OnLButtonDownの基本クラスを呼び出す処理を最初にしてみましたが、症状は変わりませ
んでした。
(ちなみに、ドキュメント/ビュー アーキテクチャは使用していませんので、
CChildViewの基本クラスは始めからCWndです)

DoDragDropの戻り値は理解しているつもりです。
コードを簡素化するためにDROPEFFECT_COPYのみを渡しています。
戻り値はGoogle Chromeにドロップしたときは想定通りDROPEFFECT_COPYが返ってきま
す。
戻り値にかかわらず、OnLButtonDown終了時にCOleDataSourceのデストラクタが適切な後
始末などをしてくれるのではないでしょうか?
DoDragDropの戻り値によって、COleDataSourceに渡したクリップボードデータを自前で
削除する必要があるのでしょうか?
COleDataSourceのソースを見ると、デストラクタでCOleDataSource::Emptyが呼ばれ、適
切な開放作業が行われているように見えます。

ロマ 2010/11/09(火) 15:53:41
MFCはわからないですが、OLEだと
IDropSource* p = new MyDropSouce;
//実装に依るけどこの段階でRefCountがゼロならばp->AddRef()する
...
DoDragDrop();
p->Release();
こんな感じで、オブジェクトの寿命はOleに任せるべきと思います。

tarou 2010/11/09(火) 16:40:28
ドロップ先がAddRefすることを考慮に入れなければならないらしいです
このアサートの意味はAddRefされているからこのまま終了してはいけない、でしょうか

参考URL
http://www.eggheadcafe.com/software/aspnet/32160159/-coledatasource.aspx

デゴルガン 2010/11/09(火) 22:20:28
[[解決]]
回答ありがとうございます。
ようやく解決いたしました。

DoDragDropが終わればオブジェクトは不要になるので、削除しても問題ないと考えてい
ました。

tarouさんの参考URLからExternalReleaseやInternalReleaseを調べてみました。
そうしたら、MSDNにサンプルがありました。
http://msdn.microsoft.com/ja-jp/library/s7s7td0c(VS.80).aspx

COleDataSourceをnewで動的生成し、DoDragDropが終わったあと、InternalReleaseを呼
ぶようです。
deleteは不要で、メモリリークもありませんでした。
CCmdTarget::InternalReleaseはCOM関係の関数のようですが、よく分かりませんでし
た。

とにかく問題なく動作するようになったので解決とします。
ご回答くださった皆さんありがとうございました。

古いスレッドには追加発言できません。