仮想リストコントロール SetItemCountExで落ちてしまいます。 

[戻る]
大泉 2005/07/12(火) 10:19:41
VC++6.0 MFC WindowsXP sp2

分割した二つのウィンドウがありまして
右側はツリー構造、左側はリストビューを利用しております。
右側のツリーフォルダを選択した時に、左側のリストビューにその中身を表示するような
システム構造になっております。
また、リストビューは仮想リストコントロールを利用します。

そこで、仮想リストコントロールのSetItemCountExでシステムエラーになって落ちてし
まいます。
エラーメッセージは



// ウィンドウ生成
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
    CCreateContext* pContext)
{
    // 分割ウィンドウを作成します
    CRect cr;    
    BOOL rc;    
    if (!m_wndSplitter.CreateStatic(this,1,2)){// 1行2列のビューを作成
        TRACE0("Failed to create split bar ");        
        return FALSE; // failed to create    
    }

    GetClientRect(&cr);    // ウィンドウの幅を取得
    CSize paneSize(230, cr.Height());                // 左
側ペインの幅を設定
    CSize paneSize1(cr.Width()-230 , cr.Height());    // 右側ペインの
幅を設定 
    ((CCADExplorerApp*)AfxGetApp())->m_pDoc=(CXXXXXDoc*)(pContext-
>m_pCurrentDoc);
    pContext->m_pCurrentFrame=this;
    rc=m_wndSplitter.CreateView(0, 0,pContext->m_pNewViewClass,paneSize, 
pContext  );
    if(!rc)return FALSE;

    pContext->m_pNewViewClass=RUNTIME_CLASS(CCADExplorerView);
    pContext->m_pCurrentDoc=((CXXXXApp*)AfxGetApp())->m_pDoc;
    pContext->m_pCurrentFrame=this;
    rc=m_wndSplitter.CreateView(0,1,pContext-
>m_pNewViewClass,paneSize1,pContext);
    m_wndSplitter.RecalcLayout();    
    m_wndSplitter.SetActivePane(0,1);
}



extern pXXXXXView;

void CLeftView::OnInitialUpdate()
{
    CTreeView::OnInitialUpdate();

    // Treeを構築
    SetTree();


    CListCtrl& ListCtrl = pXXXXXView->GetMyListCtrl();

    // ここでリストの数を設定しているが、SetItemCountExで落ちる。
    ListCtrl.SetItemCountEx( m_List.size(), LVSICF_NOINVALIDATEALL | 
LVSICF_NOSCROLL );
    ListCtrl.Invalidate();
}


// ###################################
// 落ちたときのエラー箇所 winctrl2.cpp 378行目
// ###################################
BOOL CListCtrl::SetItemCountEx(int iCount, DWORD dwFlags /* = 
LVSICF_NOINVALIDATEALL */)
{
    ASSERT(::IsWindow(m_hWnd));

    // can't have dwFlags on a control that isn't virutal
    ASSERT(dwFlags == 0 || (GetStyle() & LVS_OWNERDATA));<<======ここで落
ちる

    return (BOOL) ::SendMessage(m_hWnd, LVM_SETITEMCOUNT, (WPARAM) iCount,
        (LPARAM) dwFlags);
}

dairygoods 2005/07/12(火) 10:30:11
そこのコメントにあるとおり、
仮想リストビューでは、dwFlagsを指定してはいけないということでしょう。

Kerry 2005/07/12(火) 11:18:24
> そこのコメントにあるとおり、
> 仮想リストビューでは、dwFlagsを指定してはいけないということでしょう。

逆ですね。コメントは、仮想リストビューでない場合はdwFlagsを指定しない、
となっています。

仮想リストビューと言っておきながら、リストビューのスタイルに LVS_OWNERDATA 
スタイルが立っていないのではないですか?
PreCreateWindowで、LVS_OWNERDATAスタイルを付加するようにしましょう。

大泉 2005/07/12(火) 11:25:13
この設定をしていますが、先ほどのエラーになります。
間違っている箇所はございますか?

BOOL CXXXXView::PreCreateWindow(CREATESTRUCT& cs)
{
 int DisplayMode = atoi(GetRegistry( "DisplayMode" ));

 // 0:アイコン 1:一覧 2:詳細
 if( DisplayMode == 0 )
   cs.style |= ( LVS_ICON   | LVS_SHOWSELALWAYS | LVS_OWNERDATA );//アイコン

 if( DisplayMode == 1 ) 
   cs.style |= ( LVS_LIST   | LVS_SHOWSELALWAYS | LVS_OWNERDATA );//一覧

 if( DisplayMode == 2 ) 
   cs.style |= ( LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA );//詳細

   cs.dwExStyle |= ( LVS_EX_TWOCLICKACTIVATE | 
              LVS_EX_INFOTIP | 
              LVSICF_NOINVALIDATEALL | 
              LVSICF_NOSCROLL);


 return CListView::PreCreateWindow(cs);
}

大泉 2005/07/12(火) 11:28:09
補足として
GetRegistry( "DisplayMode" )はレジストりから、
前回、画面を終了した時、ビューの表示形式を取得している箇所なので今回のエラーと
は関係ありません。
無視してください。

dairygoods 2005/07/12(火) 11:28:21
すみません、逆でしたね。失礼しました。

Kerry 2005/07/12(火) 11:50:41
> cs.dwExStyle |= ( LVS_EX_TWOCLICKACTIVATE | 
>            LVS_EX_INFOTIP | 
>            LVSICF_NOINVALIDATEALL |    <==?
>            LVSICF_NOSCROLL);           <==?

ここが少し変ですが、今回の件とはあまり関係なさそうです。

本当に LVS_OWNERDATA スタイルが立っているのであれば、
原因はちょっとわかりません。

念のため、SetItemCountExを呼び出す前に、

ASSERT(ListCtrl.GetStyle() & LVS_OWNERDATA);

↑を入れてみてはいかがでしょうか?

大泉 2005/07/12(火) 11:54:28
ASSERT(ListCtrl.GetStyle() & LVS_OWNERDATA);を入れました。
結果ですが、
ASSERT(ListCtrl.GetStyle() & LVS_OWNERDATA);の箇所でエラーになりました。

どういう事なのでしょうか?

Kerry 2005/07/12(火) 11:55:53
別のビューにLVS_OWNERDATAスタイルを立てている、
とかいうことはないですよね?

シャノン 2005/07/12(火) 12:16:20
ぢつは GetRegistry で 0, 1, 2 以外の値が取得されちゃって、LVS_OWNERDATA が立って
ないとか。

LVS_SHOWSELALWAYS | LVS_OWNERDATA は、どの表示モードであるかにかかわらず設定する
のであれば、if 文の外に追い出しちゃうとか。

Kerry 2005/07/12(火) 12:25:36
あと、考えられる可能性としては、

>   CListCtrl& ListCtrl = pXXXXXView->GetMyListCtrl();

このpXXXXXViewやListCtrlが正しいリストビューを指しているかどうか。
強引なキャストを行っている箇所はないかどうか。

ASSERT_KINDOF(CListView, pXXXXXView);
ASSERT_KINDOF(CListCtrl, &ListCtrl);

などでチェックしてみる、とか。

大泉 2005/07/12(火) 13:02:48
ASSERT_KINDOF(CListView, pXXXXXView);の箇所で落ちる事がわかりました。
ということは、OnInitialUpdateの実行しているタイミングでは、
pXXXXXViewは実体化されていないという事のでしょうか?


CMainFrame::OnCreateClientの箇所で、
先にLeftViewをクリエイトしているので、その段階ではまだpXXXXXViewを生成していま
せん。
それが原因なのでしょうか?
wndSplitter.CreateView(0,0 -----);// ツリー側
wndSplitter.CreateView(0,1 -----);// リストビュー側

大泉 2005/07/12(火) 13:06:15
すみません、微妙に間違っていました。
先ほど、ASSERT_KINDOF(CListView, pXXXXXView);の箇所で落ちるといいましたが、
ASSERT_KINDOF(CListCtrl, &ListCtrl);で落ちてました。

Kerry 2005/07/12(火) 13:44:41
> ASSERT_KINDOF(CListCtrl, &ListCtrl);で落ちてました。

だとすると、返されたListCtrlがCListCtrlクラスではない、
ということになると思います。

>   CListCtrl& ListCtrl = pXXXXXView->GetMyListCtrl();

ここのGetMyListCtrl()はどのような実装になっているのですか?
CXXXXViewがCListView派生であるならば、
CListView::GetListCtrl()を使用していないのはなぜでしょうか?

syo 2005/07/12(火) 13:50:07
CListView::GetListCtrl() は単に *(CListCtrl*)this を返すだけの嘘つきなので、

CListCtrl& List = GetListCtrl();
ASSERT_KINDOF(CListCtrl, &List);

としてもエラーになります。
実体は、あくまでも CListView なのです。

Kerry 2005/07/12(火) 13:56:18
なるほどそうでした、失礼しましたm(__)m

大泉 2005/07/12(火) 14:41:59
CListCtrlが実態が生成していない事を確認するには、
どうすればよいでしょうか?
このような感じで実態が無い場合は処理を回避したいです。
ちなみに、if(ListCtrl != NULL ){はコンバイルエラーになりました。


CListCtrl& ListCtrl = pXXXXXView->GetMyListCtrl();
if(ListCtrl != NULL ){ <<================================== エラー
   // ここでリストの数を設定しているが、SetItemCountExで落ちる。
    ListCtrl.SetItemCountEx( m_List.size(),
                             LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL );
    ListCtrl.Invalidate();
}

シャノン 2005/07/12(火) 15:08:04
> ちなみに、if(ListCtrl != NULL ){はコンバイルエラーになりました。

ListCtrl は参照であってポインタではありませんので、NULL との比較はできません。
そもそも、参照は(余程ひねくれたことをしない限り)NULL になりません。

> CListView::GetListCtrl() は単に *(CListCtrl*)this を返すだけ

ですので、GetMyListCtrl が GetListCtrl と同じことをしているのだとすれば、
その戻り値が NULL になるケースでは this == NULL ということであり、
こういう事態は、絶対とはいえませんが、そうそうあるものではありません。

ただ、ListCtrl が何を指しているかは、GetMyListCtrl の実装によりますので、
それを示していただかないことには、進展は無いような気がします。

ところで

> ASSERT_KINDOF(CListCtrl, &ListCtrl);で落ちてました。

これはどこに書きました?

大泉 2005/07/12(火) 15:32:08


void CLeftView::OnInitialUpdate()
{
    CTreeView::OnInitialUpdate();

    // Treeを構築
    SetTree();


    CListCtrl& ListCtrl = pXXXXXView->GetMyListCtrl();
  ASSERT_KINDOF(CListView, pExplorerView);
  ASSERT_KINDOF(CListCtrl, &ListCtrl);<< --- エラー
  ASSERT(ListCtrl.GetStyle() & LVS_OWNERDATA);<< --- エラー

    // エラー
    ListCtrl.SetItemCountEx( m_List.size(), LVSICF_NOINVALIDATEALL | 
LVSICF_NOSCROLL );
    ListCtrl.Invalidate();
}

dairygoods 2005/07/12(火) 15:43:59
DisplayMode が確実に 0,1,2のいずれかになっていることが
確認されてないようですが、大丈夫でしょうか?

また、pXXXXXViewという変数に値をセットする部分は
どうなっていますか?

大泉 2005/07/12(火) 16:02:16
みなさんありがとうございました。
無事解決いたしました。
dairygoodsさんの助言の通り、DisplayModeに変な値が入っていて
0,1,2以外の数字になっていました。
私のミスです。
今回のカキコミでASSERT_KINDOFの使い方や、バグの解決の仕方など
色々勉強になりました。
またわからない事があれば、その時は御願いいたします。


>DisplayMode が確実に 0,1,2のいずれかになっていることが
>確認されてないようですが、大丈夫でしょうか?

大泉 2005/07/12(火) 16:02:28
[[解決]]
解決

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