システムアイコンのイメージリストを2つのビューで個別に保持するには

[戻る]
2005/02/13(日) 15:47:56
こんにちは
連日質問ばかりですみませんがよろしくお願いします

開発環境:WinXP、VC++6.0、MFC、SDI

現在メインフレームをCSplitterWndによって2つに分割し、
CTreeViewとCListView(実際はそれぞれの派生クラス)を割り当てています

今まではTreeViewのみに、システムのイメージリスト(スモールアイコン)を設定して
いました
ここはでは問題がなかったのですが、ListViewに着手し、
ListViewにも同じイメージリストを設定しようとしたところで問題が発生しました

現在のそれぞれのイメージリスト設定関数は以下のようになっています

//■■■CTreeView(の派生クラス)側■■■
BOOL CFolderView::SetSysIconList()
{
    HIMAGELIST    hSmallIconList;
    SHFILEINFO    SHFileInfo;

    hSmallIconList = (HIMAGELIST)SHGetFileInfo("",0,&SHFileInfo,sizeof
(SHFILEINFO),
                SHGFI_SYSICONINDEX|SHGFI_SMALLICON);
    if(hSmallIconList== NULL)
        return FALSE;

    m_SysIconList.Attach(hSmallIconList);
    CTreeCtrl&    Tree    = GetTreeCtrl();
    Tree.SetImageList(&m_SysIconList,TVSIL_NORMAL);

    return TRUE;
}

//■■■CListView(の派生クラス)側■■■
BOOL CFileView::SetSysIconList()
{
    HIMAGELIST    hSmallIconList;
    HIMAGELIST    hLargeIconList;
    SHFILEINFO    SHFileInfo;

    hSmallIconList = (HIMAGELIST)SHGetFileInfo("",0,&SHFileInfo,sizeof
(SHFILEINFO),
                SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
    hLargeIconList = (HIMAGELIST)SHGetFileInfo("",0,&SHFileInfo,sizeof
(SHFILEINFO),
                SHGFI_SYSICONINDEX | SHGFI_LARGEICON);

    if(hSmallIconList == NULL || hLargeIconList == NULL)
        return FALSE;

    m_SysSmallIconList.Attach(hSmallIconList);
    m_SysLargeIconList.Attach(hLargeIconList);

    CListCtrl&    List    = GetListCtrl();
    List.SetImageList(&m_SysSmallIconList,LVSIL_SMALL);
    List.SetImageList(&m_SysLargeIconList,LVSIL_NORMAL);

    return TRUE;

}

頭にm_がついているのはそれぞれのクラスのメンバ変数です

これをビルドして実行すると、実行時エラーで落ちます

で、自分で調べてみた限りでは、どうやら別々のCImageListオブジェクトに
同じイメージリスト(システムイメージのスモールアイコン)をAttachしているのが
いけないのではないのかと思っています

全てのSetImageList()を消してもエラーは出ますが、
どちらか一方でもCImageList::Attach(hSmallIconList)を消すとエラーが消えるからで
す

そこで解決策を模索してみた結果、2つほど案が浮かびました

まず最初に見つけたのが
CImageList::FromHandlePermanent(hImageList)
というものでした

これを使って

CImageList    pImageList;
pImageList    = CImageList::FromHandlePermanent(hImageList)
if(pImageList != NULL)
{
    Tree.SetImageList(pImageList),TVSIL_NORMAL);
}
else{
    m_SysIconList.Attach(hImageList);
    Tree.SetImageList(&m_SysIconList,TVSIL_NORMAL);
}

このようにすることで、先に初期化されたビューのCImageListオブジェクトの
ポインタを得ることができ、それを設定することである意味共有はできました
それぞれのビューにきちんとアイコンが表示されます

しかし(現在作成しているアプリでは大丈夫ですが)もしもイメージリストを保持して
いる
ビューが途中で破棄されてしまった場合にイメージリストが消えてしまう気がします
それに他のビューのメンバ変数にあるCImageListのポインタを使うといこと自体
何か気持ち悪い気もしますし

で、もう一つ考えたのは、CMainFrameにイメージリストを持たせ、
それをそれぞれのビューから参照するというものです

これならアプリが起動されてから終了されるまでイメージリストは存在し続けます
(たぶんですが。こっちはまだ試していません)

しかし、できればそれぞれのビューで独立した動作をさせたいので、
可能ならそれぞれのビューに個別のイメージリストを持たせたいのです

このようなことは可能なのでしょうか?
何か知っている方がいましたらアドバイスをお願いします

2005/02/13(日) 15:56:42
すいません、ちょっと訂正です

> これをビルドして実行すると、実行時エラーで落ちます

と書きましたが

「Microsoft Visual C++ Debug Library」というタイトルのダイアログが出て
Debug Assertion Failed!
Program: ・・・App.exe
File: winctrl2.cpp
Line: 1203
・・・略・・・
「中止」「再試行」「無視」

と言われます

出るタイミングはアプリを起動して、ウインドウが表示される前です
無視を押せばウインドウは表示され、一応は両方のビューにアイコンは表示されます
ちなみに何も修正してないコードの場合の話です
(それぞれのビューで個別にAttach()している)

以上です
よろしくお願いします

dairygoods 2005/02/13(日) 16:21:04
A)共有でよい場合
・CImageListメンバは用意しない。
・どちらのビューでもCImageList::FromHandle()でポインタを取得する。
  (この場合、実体はどこか見えないところにMFCが適当に確保します)
・リストビューに「共有イメージリスト」というフラグがあるので立てておく。

B)異なるリストを保持したい場合
・両方にCImageListメンバを宣言する。
・SHGetFileInfoではSHGFI_SYSICONINDEXフラグを指定しないで、
 アイコンハンドルを取得し、必要な方に(または両方に)Addする。

2005/02/13(日) 16:55:03
dairygoodsさん、レスありがとうございます

提案していただいた2つの方法のうち、Bに関しましては
プログラムの構造上問題が出てくるので採用はできません
(できなくもないですが、Aの方がはるかに楽になる)

そこでAの方法を試してみたところ、うまくいきました

調べてみたところ
CImageList::FromHandle()では一時オブジェクトを作成するようですが、
結局CTreeCtrl::SetImageList()などではpImageList->GetSafeHandle()という形で
使っているだけのようなので、つまり一時オブジェクトは消えてしまっても
問題ないということなんでしょうか

あとここで追加質問しようかどうか迷ったのですが、
関連している内容だと思うので質問させてください

素朴な疑問なのですが、リストビューにはLVS_SHAREIMAGELISTSという
dairygoodsさんもおっしゃっていますが、共有イメージリストであることを表すフラグ
がありますよね
しかしツリービューにはそれと同じようなフラグは見当たりません

リストビューではこの共有フラグをつけなかった場合、
関連付けられているイメージリストは破棄されるようですが、
ツリービューでは(そもそもフラグはありませんが)破棄されないようです

このようにリストビューにだけこの共有フラグがあるのは何か理由があるのでしょう
か?

知らなくても特に支障が出るような事ではないかもしれませんが、
何か妙に気になるのでもし知っている方いましたら教えてもらえないでしょうか
よろしくお願いします

dairygoods 2005/02/13(日) 18:12:51
> 一時オブジェクトは消えてしまっても問題ないということなんでしょうか

MFCのオブジェクトは、Win32ハンドル(イメージリストならHIMAGELIST)の
単なるラップなので、ハンドル自体を削除しない限り、
MFCオブジェクトは無くなっても問題ありません。

(派生してメンバ変数を追加したりした場合は異なります)

2005/02/13(日) 20:01:19
[[解決]]
お返事ありがとうございます

> …略…
> MFCオブジェクトは無くなっても問題ありません。

ですよね
まだMFCは経験が浅いので確認しておきたかったんです

本題に関しては解決しましたので解決チェックをつけておきますが、
もし「リストビューにだけこの共有フラグがある理由」を知っている方がいましたら
こっそり教えていただけるとありがたいです

どうもありがとうございました

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