none
CWndのAttachメソッドを使わずにm_hWndに直接ウィンドウハンドルを代入した場合の弊害について RRS feed

  • 質問

  • 現在、SysListView32(MFCでいうところのCListCtrl)をラッピングしたActiveXコントロールを作成しています。
    SysListView32に対する操作を簡単にするために、CListCtrlクラス経由で操作しようと考えました。

    ActiveXコントロールの作成の際、VisualStudioのMFC ActiveXコントロールでプロジェクトを作成しました。
    この場合、コントロールクラスの基底クラスがCOleControlになります。

    まず、このコントロールにCListCtrlクラスをメンバ変数として追加しました。仮に、m_ListCtrlとします。

    そして、コントロールクラスのOnCreateを追加し、以下のようなコーディングを行いました。

    m_ListCtrl.Attach(this->GetSafeHWnd());

    そして、いざ実行するとエラーが発生しました。

    原因を調べたところ、同時にAttachできるウィンドウの数は1つまでだということが分かりました。
    (当たり前ですが、既にコントロールクラス自身にアタッチされている。)

    代替案として、m_ListCtrlm_hWndに直接ウィンドウハンドルを設定することを思いつきました。
    で、実際にOnCreateを以下のようなコードに変えて、CListCtrlの適当なメソッドを実行してみたところ、問題なく動作しました。

    m_ListCtrl.m_hWnd = this->GetSafeHWnd();

    あまりにも安易な方法を実践しているため、これで本当に問題ないのかというのが気になるところですが、いかがでしょうか?
    また、皆さんはこのようなケースを経験したことはありますでしょうか?もし、経験したことがあるのであれば、実際にどのように対処したかなどをご教授頂ければと思います。

    ※ちなみに、m_ListCtrlはprotectedまたはprivateとして、クラス内部でのみ使用します。
     クラス内部で利用するのであれば、特に問題はなさそう、と個人的に思ったりもしています・・・。

    以上、宜しくお願いいたします。

    2009年6月8日 16:16

回答

  • CListCtrlのm_hWndを使ったただのラッパ関数は動作するでしょう。

    しかし、WindowsメッセージがこのCListCtrlにディスパッチされることはありません。
    そのため、たとえばCListCtrl::OnNcDestroy()で行っているイメージリストの解放などは、
    自前で処理する必要がありますし、
    MFCのメッセージマップを使ったハンドラ関数を記述することはできません。

    また、CListCtrlのデストラクタではDestroyWindow()呼び出しを行っていますので、
    つじつまを合わせる必要がある可能性があったり、そういった困ることはあると思います。

    それ以外にも、MFCがスレッド内に保持するHWND<=>CWndのマップは1対1で対応することに
    なっていますから、それを仮定したMFCのコードが正常に動作しない可能性があります。
    たとえば、CWnd::AssertValid()では普通にAssertされます。

    CListCtrlのソースを見て、ご自分の使い方と照らし合わせて大丈夫と判断されるなら
    それはそれでいいのかもしれませんが、私ならやりません。
    • 回答としてマーク さんど 2009年6月9日 15:21
    2009年6月9日 6:50
  • 皆さんの回答で一番多かった、m_ListCtrlのCreateについてなんですが、Createは一切していません。
    m_ListCtrlを使う理由は、COleControl をCListCtrlとして扱うためです。
    あ、すみません。最初の文章を読み違えていました。
    原因を調べたところ、同時にAttachできるウィンドウの数は1つまでだということが分かりました。
    (当たり前ですが、既にコントロールクラス自身にアタッチされている。)
    この部分、
    ・m_ListCtrl に同時に Attach できる HWND の数が1つまでで、既に m_ListCtrl に HWND がアタッチされている
    ではなくて、
    ・ある HWND を Attach できる CWnd は1つまでで、既に this->m_hWnd は COleControl にアタッチされている
    だったんですね。

    それなら、this->Detach() を呼んでから……という冗談は置いといて、CListCtrl をラッパとして「安全に」使いたい場合は、使いたいメンバ関数の中身をいちいち確認するという作業が必要になるはずです。
    どうせそこまで手間をかけるなら、CListCtrl の中身をマネしたラッパを自作した方が安心です。

    「あまりにも安易な方法を実践しているため、これで本当に問題ないのか」については、

    ・大丈夫なメンバ関数もあるし、そうでないものもある
    ・CListCtrl のデストラクタが呼ばれる前に m_hWnd をクリアしておくことは必須

    といったところでしょうか。
    • 回答としてマーク さんど 2009年6月9日 15:20
    2009年6月9日 14:19

すべての返信

  • まず、このコントロールにCListCtrlクラスをメンバ変数として追加しました。仮に、m_ListCtrl とします。

    そして、コントロールクラスのOnCreateを追加し、以下のようなコーディングを行いました。

    m_ListCtrl.Attach(this->GetSafeHWnd());

    そして、いざ実行するとエラーが発生しました。

    原因を調べたところ、同時にAttachできるウィンドウの数は1つまでだということが分かりました。
    (当たり前ですが、既にコントロールクラス自身にアタッチされている。)

    代替案として、m_ListCtrlm_hWnd に直接ウィンドウハンドルを設定することを思いつきました。
    で、実際にOnCreateを以下のようなコードに変えて、CListCtrlの適当なメソッドを実行してみたところ、問題なく動作しました。

    m_ListCtrl.m_hWnd = this->GetSafeHWnd();

    あまりにも安易な方法を実践しているため、これで本当に問題ないのかというのが気になるところですが、いかがでしょうか?
    また、皆さんはこのようなケースを経験したことはありますでしょうか?もし、経験したことがあるのであれば、実際にどのように対処したかなどをご教授頂ければと思います。

    ※ちなみに、m_ListCtrl はprotectedまたはprivateとして、クラス内部でのみ使用します。
    クラス内部で利用するのであれば、特に問題はなさそう、と個人的に思ったりもしています・・・。

    既にコントロールクラス自身にアタッチされていると言う部分が良くわかりません。
    アタッチするつもりのm_ListCtrlをCreateしていると言う事ですか?
    Createしてしまうとウインドウが生成されてしまうのでアタッチ済みになるのもわかるのですが、
    アタッチしようと考えているならなぜCreateしているのかが良く分かりません。

    また、既にアタッチ済みなのであれば、既にm_ListCtrlのm_hWndに入っていたウインドウハンドルはどうなるんでしょう?
    上書きされてしまったら行方不明になってしまうと思うのですけれど。

    しようとしている事と実際にしている事の間に不整合があるように思います。
    やり方自体に問題はないでしょうか?


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2009年6月9日 2:34
  • m_ListCtrl の Create を呼ばなければ、ウィンドウハンドルは空のはずです。
    Attach に失敗するということは、どこかで Create (or CreateEx or Attach) しているのではないでしょうか。
    その部分を見つけてコメントアウトする方が良いと思います。

    m_hWnd を直接書き換えてしまうと、その前のウィンドウハンドルに対する終了処理がすっ飛ばされてしまいます。
    Create なら DestroyWindow でやっている処理が、Attach なら Detach の処理が飛ばされる対象です。
    ここでは m_hWnd を NULL にする以外にも色々やっていますので、何か問題があってもおかしくありません。
    (問題があるとしてもどうせ終了時なので、大したことはないかも知れませんが……)
    2009年6月9日 2:41
  • 非常に初歩的な部分の指摘から。。。COleControl は一種のフレームウィンドウです。実際にウィンドウとして存在しており、ラップしたリストビューコントロールの親ウィンドウとしてひっそりと存在しています。
    なので、その親ウィンドウを語にょごにょやろうと思ってもリストビューコントロールではありませんので、思った処理はできません。
    ということなので、考え方とかいろいろ変えてください。

    さて、表題の部分の Attachせずに m_hWnd に入れたらどうなるか?ですが、まず、FromHandle系のメソッド呼び出しがことごとく失敗します。
    クラスメソッドを呼び出している分には、めったなことで失敗はしませんが。。。

    このあたりは、とりあえず Attach が中で何をやっているのかをデバッガでステップ実行するなりなんなりして、調査してみることをお勧めします。


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    2009年6月9日 4:10
  • CListCtrlのm_hWndを使ったただのラッパ関数は動作するでしょう。

    しかし、WindowsメッセージがこのCListCtrlにディスパッチされることはありません。
    そのため、たとえばCListCtrl::OnNcDestroy()で行っているイメージリストの解放などは、
    自前で処理する必要がありますし、
    MFCのメッセージマップを使ったハンドラ関数を記述することはできません。

    また、CListCtrlのデストラクタではDestroyWindow()呼び出しを行っていますので、
    つじつまを合わせる必要がある可能性があったり、そういった困ることはあると思います。

    それ以外にも、MFCがスレッド内に保持するHWND<=>CWndのマップは1対1で対応することに
    なっていますから、それを仮定したMFCのコードが正常に動作しない可能性があります。
    たとえば、CWnd::AssertValid()では普通にAssertされます。

    CListCtrlのソースを見て、ご自分の使い方と照らし合わせて大丈夫と判断されるなら
    それはそれでいいのかもしれませんが、私ならやりません。
    • 回答としてマーク さんど 2009年6月9日 15:21
    2009年6月9日 6:50
  • 多数の返信有難うございます。返信というより突っ込みといったが方が合っているかもしれませんね。f(^^;

    皆さんの回答で一番多かった、m_ListCtrlのCreateについてなんですが、Createは一切していません。
    m_ListCtrlを使う理由は、COleControl をCListCtrlとして扱うためです。

    当然以下のように、キャストはできませんので、m_ListCtrlにアタッチしようと考えました。

    CListCtrl m_ListCtrl = (CListCtrl)this; (thisはCOleControl コントロールクラスです)

    次にとっちゃんさんの指摘なんですが。

    >非常に初歩的な部分の指摘から。。。COleControl は一種のフレームウィンドウです。実際にウィンドウとして存在しており、ラップしたリストビューコントロールの親ウィンドウとしてひっそりと存在しています。
    >なので、その親ウィンドウを語にょごにょやろうと思ってもリストビューコントロールではありませんので、思った処理はできません。
    >ということなので、考え方とかいろいろ変えてください。

    私としましては、COleControlクラスはリストビューコントロール自身なのではないかと考えております。
    何故、そう思ったのかというとPreCreateWindowのクラス名の指定で"SysListView32"と指定しているからです。(ちなみに、これは、ウィザードが勝手に埋め込んだものです。)

    ※Spy++でも調べてみましたが、リストビューがフレームウィンドウとして覆われている感じはありませんでした。

    少し長いですが、コントロールクラスのソースを貼り付けます。注目して頂きたい部分に色づけしておきました。

    #pragma once
    
    // IsonListBoxCtrl.h : CIsonListBoxCtrl ActiveX コントロール クラスの宣言です。
    
    
    // CIsonListBoxCtrl : 実装に関しては IsonListBoxCtrl.cpp を参照してください。
    
    class CIsonListBoxCtrl : public COleControl
    {
    	DECLARE_DYNCREATE(CIsonListBoxCtrl)
    
    // コンストラクタ
    public:
    	CIsonListBoxCtrl();
    
    // オーバーライド
    public:
    	virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
    	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    	virtual void DoPropExchange(CPropExchange* pPX);
    	virtual void OnResetState();
    
    // 実装
    protected:
    	~CIsonListBoxCtrl();
    
    	DECLARE_OLECREATE_EX(CIsonListBoxCtrl)    // クラス ファクトリ と guid
    	DECLARE_OLETYPELIB(CIsonListBoxCtrl)      // GetTypeInfo
    	DECLARE_PROPPAGEIDS(CIsonListBoxCtrl)     // プロパティ ページ ID
    	DECLARE_OLECTLTYPE(CIsonListBoxCtrl)		// タイプ名とその他のステータス
    
    	// サブクラス化されたコントロールのサポート
    	BOOL IsSubclassedControl();
    	LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam);
    
    // メッセージ マップ
    	DECLARE_MESSAGE_MAP()
    
    // ディスパッチ マップ
    	DECLARE_DISPATCH_MAP()
    
    	afx_msg void AboutBox();
    
    // イベント マップ
    	DECLARE_EVENT_MAP()
    
    // ディスパッチ と イベント ID
    public:
    	enum {
            dispidGetColumn = 18L,
            dispidSetColumn = 17L,
            dispidGetItem = 16L,
            dispidSetItem = 15L,
            dispidClearColumn = 14L,
            dispidRemoveColumn = 13L,
            dispidAddColumn = 12L,
            dispidRemoveItem = 9L,
            dispidClear = 8L,
            dispidAddItem = 7L,
            dispidValue = 6,
            dispidSelected = 5,
            dispidMultiSelect = 4,
            dispidListStyle = 3,
            dispidListIndex = 2,
            dispidListCount = 1
        };
    protected:
        LONG GetListCount(void);
        LONG GetListIndex(void);
        void SetListIndex(LONG newVal);
        LONG GetListStyle(void);
        void SetListStyle(LONG newVal);
        VARIANT_BOOL GetMultiSelect(void);
        void SetMultiSelect(VARIANT_BOOL newVal);
        VARIANT_BOOL GetSelected(LONG index);
        void SetSelected(LONG index, VARIANT_BOOL newVal);
        BSTR GetValue(void);
        void SetValue(LPCTSTR newVal);
        void AddItem(LONG row, LONG column, LPCTSTR value);
        void Clear(void);
        void RemoveItem(LONG row);
    
    // ユーザ定義メンバ
    protected:
    
        /**
         * リストコントロール。
         * リストコントロールのメソッドを簡単に利用するために
         * WM_CREATE 時にアタッチし、WM_DESTROY 時にデタッチする。
         */
        CListCtrl listCtrl;
    
        /**
         * 複数選択
         */
        VARIANT_BOOL m_MultiSelect;
    
    // ユーザ定義メソッド
    protected:
    
    
    public:
        afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
        afx_msg void OnDestroy();
    protected:
        void AddColumn(LONG column, LPCTSTR value);
        void RemoveColumn(LONG column);
        void ClearColumn(void);
        void SetItem(LONG row, LONG column, LPCTSTR value);
        BSTR GetItem(LONG row, LONG column);
        void SetColumn(LONG column, LPCTSTR value);
        BSTR GetColumn(LONG column);
    };
    
    // IsonListBoxCtrl.cpp :  CIsonListBoxCtrl ActiveX コントロール クラスの実装
    
    #include "stdafx.h"
    #include "IsonListBox.h"
    #include "IsonListBoxCtrl.h"
    #include "IsonListBoxPropPage.h"
    
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    IMPLEMENT_DYNCREATE(CIsonListBoxCtrl, COleControl)
    
    
    
    // メッセージ マップ
    
    BEGIN_MESSAGE_MAP(CIsonListBoxCtrl, COleControl)
    	ON_MESSAGE(OCM_COMMAND, &CIsonListBoxCtrl::OnOcmCommand)
    	ON_OLEVERB(AFX_IDS_VERB_EDIT, OnEdit)
    	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
        ON_WM_CREATE()
        ON_WM_DESTROY()
    END_MESSAGE_MAP()
    
    
    
    // ディスパッチ マップ
    
    BEGIN_DISPATCH_MAP(CIsonListBoxCtrl, COleControl)
    	DISP_FUNCTION_ID(CIsonListBoxCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
        DISP_PROPERTY_EX_ID(CIsonListBoxCtrl, "ListCount", dispidListCount, GetListCount, SetNotSupported, VT_I4)
        DISP_PROPERTY_EX_ID(CIsonListBoxCtrl, "ListIndex", dispidListIndex, GetListIndex, SetListIndex, VT_I4)
        DISP_PROPERTY_EX_ID(CIsonListBoxCtrl, "ListStyle", dispidListStyle, GetListStyle, SetListStyle, VT_I4)
        DISP_PROPERTY_EX_ID(CIsonListBoxCtrl, "MultiSelect", dispidMultiSelect, GetMultiSelect, SetMultiSelect, VT_BOOL)
        DISP_PROPERTY_PARAM_ID(CIsonListBoxCtrl, "Selected", dispidSelected, GetSelected, SetSelected, VT_BOOL, VTS_I4)
        DISP_PROPERTY_EX_ID(CIsonListBoxCtrl, "Value", dispidValue, GetValue, SetValue, VT_BSTR)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "AddItem", dispidAddItem, AddItem, VT_EMPTY, VTS_I4 VTS_I4 VTS_BSTR)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "Clear", dispidClear, Clear, VT_EMPTY, VTS_NONE)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "RemoveItem", dispidRemoveItem, RemoveItem, VT_EMPTY, VTS_I4)
        DISP_STOCKPROP_APPEARANCE()
        DISP_STOCKPROP_BACKCOLOR()
        DISP_STOCKPROP_BORDERSTYLE()
        DISP_STOCKPROP_ENABLED()
        DISP_STOCKPROP_FONT()
        DISP_STOCKPROP_FORECOLOR()
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "AddColumn", dispidAddColumn, AddColumn, VT_EMPTY, VTS_I4 VTS_BSTR)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "RemoveColumn", dispidRemoveColumn, RemoveColumn, VT_EMPTY, VTS_I4)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "ClearColumn", dispidClearColumn, ClearColumn, VT_EMPTY, VTS_NONE)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "SetItem", dispidSetItem, SetItem, VT_EMPTY, VTS_I4 VTS_I4 VTS_BSTR)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "GetItem", dispidGetItem, GetItem, VT_BSTR, VTS_I4 VTS_I4)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "SetColumn", dispidSetColumn, SetColumn, VT_EMPTY, VTS_I4 VTS_BSTR)
        DISP_FUNCTION_ID(CIsonListBoxCtrl, "GetColumn", dispidGetColumn, GetColumn, VT_BSTR, VTS_I4)
    END_DISPATCH_MAP()
    
    
    
    // イベント マップ
    
    BEGIN_EVENT_MAP(CIsonListBoxCtrl, COleControl)
    END_EVENT_MAP()
    
    
    
    // プロパティ ページ
    
    // TODO: プロパティ ページを追加して、BEGIN 行の最後にあるカウントを増やしてください。
    BEGIN_PROPPAGEIDS(CIsonListBoxCtrl, 1)
    	PROPPAGEID(CIsonListBoxPropPage::guid)
    END_PROPPAGEIDS(CIsonListBoxCtrl)
    
    
    
    // クラス ファクトリおよび GUID を初期化します。
    
    IMPLEMENT_OLECREATE_EX(CIsonListBoxCtrl, "ISONLISTBOX.IsonListBoxCtrl.1",
    	0xc9a96914, 0x9ed, 0x4de1, 0xa4, 0x3e, 0xaa, 0x1a, 0x62, 0x2d, 0xe2, 0x81)
    
    
    
    // タイプ ライブラリ ID およびバージョン
    
    IMPLEMENT_OLETYPELIB(CIsonListBoxCtrl, _tlid, _wVerMajor, _wVerMinor)
    
    
    
    // インターフェイス ID
    
    const IID BASED_CODE IID_DIsonListBox =
    		{ 0xB875F6ED, 0xA77A, 0x471F, { 0xBD, 0x69, 0x8B, 0xC2, 0xF1, 0x97, 0x91, 0x2D } };
    const IID BASED_CODE IID_DIsonListBoxEvents =
    		{ 0x87B50884, 0x56FA, 0x466D, { 0xBC, 0xF0, 0x88, 0x85, 0xF4, 0x7A, 0xEF, 0x62 } };
    
    
    
    // コントロールの種類の情報
    
    static const DWORD BASED_CODE _dwIsonListBoxOleMisc =
    	OLEMISC_ACTIVATEWHENVISIBLE |
    	OLEMISC_SETCLIENTSITEFIRST |
    	OLEMISC_INSIDEOUT |
    	OLEMISC_CANTLINKINSIDE |
    	OLEMISC_RECOMPOSEONRESIZE;
    
    IMPLEMENT_OLECTLTYPE(CIsonListBoxCtrl, IDS_ISONLISTBOX, _dwIsonListBoxOleMisc)
    
    
    
    // CIsonListBoxCtrl::CIsonListBoxCtrlFactory::UpdateRegistry -
    // CIsonListBoxCtrl のシステム レジストリ エントリを追加または削除します。
    
    BOOL CIsonListBoxCtrl::CIsonListBoxCtrlFactory::UpdateRegistry(BOOL bRegister)
    {
    	// TODO: コントロールが apartment モデルのスレッド処理の規則に従っていることを
    	// 確認してください。詳細については MFC のテクニカル ノート 64 を参照してください。
    	// アパートメント モデルのスレッド処理の規則に従わないコントロールの場合は、6 番目
    	// のパラメータを以下のように変更してください。
    	// afxRegInsertable | afxRegApartmentThreading を afxRegInsertable へ変更してください。
    
    	if (bRegister)
    		return AfxOleRegisterControlClass(
    			AfxGetInstanceHandle(),
    			m_clsid,
    			m_lpszProgID,
    			IDS_ISONLISTBOX,
    			IDB_ISONLISTBOX,
    			afxRegInsertable | afxRegApartmentThreading,
    			_dwIsonListBoxOleMisc,
    			_tlid,
    			_wVerMajor,
    			_wVerMinor);
    	else
    		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
    }
    
    
    
    // CIsonListBoxCtrl::CIsonListBoxCtrl - コンストラクタ
    
    CIsonListBoxCtrl::CIsonListBoxCtrl()
    {
    	InitializeIIDs(&IID_DIsonListBox, &IID_DIsonListBoxEvents);
    	// TODO: この位置にコントロールのインスタンス データの初期化処理を追加してください
    }
    
    
    
    // CIsonListBoxCtrl::~CIsonListBoxCtrl - デストラクタ
    
    CIsonListBoxCtrl::~CIsonListBoxCtrl()
    {
    	// TODO: この位置にコントロールのインスタンス データの後処理用のコードを追加してください
    }
    
    
    
    // CIsonListBoxCtrl::OnDraw - 描画関数
    
    void CIsonListBoxCtrl::OnDraw(
    			CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
    {
    	if (!pdc)
    		return;
    
    	DoSuperclassPaint(pdc, rcBounds);
    }
    
    
    
    // CIsonListBoxCtrl::DoPropExchange - 永続性のサポート
    
    void CIsonListBoxCtrl::DoPropExchange(CPropExchange* pPX)
    {
    	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
    	COleControl::DoPropExchange(pPX);
    
    	// TODO: 永続属性を持つ各カスタム プロパティ用の PX_ 関数を呼び出します。
    }
    
    
    
    // CIsonListBoxCtrl::OnResetState - コントロールを既定の状態にリセットします。
    
    void CIsonListBoxCtrl::OnResetState()
    {
    	COleControl::OnResetState();  // DoPropExchange を呼び出して既定値にリセット
    
    	// TODO: この位置にコントロールの状態をリセットする処理を追加してください
    }
    
    
    
    // CIsonListBoxCtrl::AboutBox - "バージョン情報" ボックスをユーザーに表示します。
    
    void CIsonListBoxCtrl::AboutBox()
    {
    	CDialog dlgAbout(IDD_ABOUTBOX_ISONLISTBOX);
    	dlgAbout.DoModal();
    }
    
    
    
    // CIsonListBoxCtrl::PreCreateWindow - CreateWindowEx のパラメータを変更します。
    
    BOOL CIsonListBoxCtrl::PreCreateWindow(CREATESTRUCT& cs)
    {
        // ウィザードによって以下のようにクラス指定がなされていた
        cs.lpszClass = _T("SysListView32");
    
        // ------------------------------------------------
        // この辺は自分で埋め込んだ
        // 複数選択可能
        if (m_MultiSelect == TRUE) {
    
            //cs.style = cs.style & (cs.style & ~(LVS_SINGLESEL));
        } else {
    
            // 単一選択
            //cs.style = cs.style | LVS_SINGLESEL;
        }
    
        // 定数    解説
        // LVS_ALIGNLEFT    大きいアイコン、小さいアイコン表示で項目が左寄せになる
        // LVS_ALIGNTOP    大きいアイコン、小さいアイコン表示で項目が上に寄せられる
        // LVS_AUTOARRANGE    大きいアイコン、小さいアイコン表示で自動的に整列する
        // LVS_BUTTON    大きいアイコン表示で、項目アイコンをボタン風にする
        // LVS_EDITLABELS    テキストは編集可能である
        // 親ウィンドウが LVN_ENDLABELEDIT 通知を処理しなければならない
        // LVS_ICON    大きいアイコン表示
        // LVS_LIST    一覧表示
        // LVS_NOCOLUMNHEADER    詳細表示で、列見出しが表示されない
        // LVS_NOLABELWRAP    大きいアイコン表示で項目テキストを1行に表示
        // LVS_NOSORTHEADER    列見出しをクリックしてもアクションを実行しない
        // LVS_OWNERDRAWFIXED    詳細表示で、項目をオーナーが描画する
        // 親ウィンドウは WM_DRAWITEM メッセージを受けるようになる
        // LVS_REPORT    詳細表示
        // 最初の列が常に左寄せにされる
        // LVS_SHAREIMAGELISTS    コントロールが破棄されても
        // 関連付けられているイメージリストを破棄しない
        // 複数のリストビューでイメージリストを共有する時に指定する
        // LVS_SHOWSELALWAYS    フォーカスを失っても、常に選択を表示する
        // LVS_SINGLESEL    1度に1つの項目のみを選択できる
        // LVS_SMALLICON    小さいアイコン表示
        // LVS_SORTASCENDING    項目テキストが昇順になるように、項目を整列する
        // LVS_SORTDESCENDING    項目テキストが降順になるように、項目を整列する
        cs.style = cs.style
                | WS_VISIBLE  // 表示する
                | LVS_REPORT; // 詳細表示
    
    
        
        return COleControl::PreCreateWindow(cs);
    }
    
    int CIsonListBoxCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (COleControl::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        //// TODO:  ここに特定な作成コードを追加してください。
        // アタッチすると失敗するので、m_hWndに直接代入している
        listCtrl.m_hWnd = this->GetSafeHwnd();
    
        return 0;
    }
    
    void CIsonListBoxCtrl::OnDestroy()
    {
        COleControl::OnDestroy();
    
        // TODO: ここにメッセージ ハンドラ コードを追加します。
        listCtrl.m_hWnd = NULL;
    
    }
    
    // CIsonListBoxCtrl::IsSubclassedControl - これはサブクラス コントロールです。
    
    BOOL CIsonListBoxCtrl::IsSubclassedControl()
    {
    	return TRUE;
    }
    
    
    
    // CIsonListBoxCtrl::OnOcmCommand - コマンド メッセージを処理します。
    
    LRESULT CIsonListBoxCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
    {
    #ifdef _WIN32
    	WORD wNotifyCode = HIWORD(wParam);
    #else
    	WORD wNotifyCode = HIWORD(lParam);
    #endif
    
    	// TODO: この位置にスイッチ ステートメントで wNotifyCode を処理するコードを追加してください
    
    	return 0;
    }
    
    ・・・省略
    
    void CIsonListBoxCtrl::AddItem(LONG row, LONG column, LPCTSTR value)
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
        // TODO: ここにディスパッチ ハンドラ コードを追加してください。
        // 省略
        if (row == -1) {
    
            // 最後尾に項目を追加する
            row = listCtrl.GetItemCount();
        }
    
        // 省略
        if (column == -1) {
    
            // 1番目を指定する
            column = 0;
        }
    
    
        LVITEM lvItem;
        lvItem.mask = LVIF_TEXT;
        lvItem.iItem = row;
        lvItem.iSubItem = column;
        lvItem.pszText = (LPWSTR)value;
    
        if (listCtrl.InsertItem(&lvItem) == -1) {
    
            // エラー処理
        }
    
    }
    
    ・・・省略
    
    

    以上を踏まえて、再度回答いただけますでしょうか。

    宜しくお願いいたします。
    • 編集済み さんど 2009年6月9日 7:15
    2009年6月9日 7:03
  • 皆さんの回答で一番多かった、m_ListCtrlのCreateについてなんですが、Createは一切していません。
    m_ListCtrlを使う理由は、COleControl をCListCtrlとして扱うためです。
    あ、すみません。最初の文章を読み違えていました。
    原因を調べたところ、同時にAttachできるウィンドウの数は1つまでだということが分かりました。
    (当たり前ですが、既にコントロールクラス自身にアタッチされている。)
    この部分、
    ・m_ListCtrl に同時に Attach できる HWND の数が1つまでで、既に m_ListCtrl に HWND がアタッチされている
    ではなくて、
    ・ある HWND を Attach できる CWnd は1つまでで、既に this->m_hWnd は COleControl にアタッチされている
    だったんですね。

    それなら、this->Detach() を呼んでから……という冗談は置いといて、CListCtrl をラッパとして「安全に」使いたい場合は、使いたいメンバ関数の中身をいちいち確認するという作業が必要になるはずです。
    どうせそこまで手間をかけるなら、CListCtrl の中身をマネしたラッパを自作した方が安心です。

    「あまりにも安易な方法を実践しているため、これで本当に問題ないのか」については、

    ・大丈夫なメンバ関数もあるし、そうでないものもある
    ・CListCtrl のデストラクタが呼ばれる前に m_hWnd をクリアしておくことは必須

    といったところでしょうか。
    • 回答としてマーク さんど 2009年6月9日 15:20
    2009年6月9日 14:19
  • zaikoさん

    回答有難うございます。

    最後に私が記入した内容が、正しくHTML化されていないようで、申し訳ないです。(編集モードでは正しく表示されていたんですが・・・。)

    それなら、this->Detach() を呼んでから……という冗談は置いといて、CListCtrl をラッパとして「安全に」使いたい場合は、使いたいメンバ関数の中身をいちいち確認するという作業が必要になるはずです。
    どうせそこまで手間をかけるなら、CListCtrl の中身をマネしたラッパを自作した方が安心です。

    「あまりにも安易な方法を実践しているため、これで本当に問題ないのか」については、

    ・大丈夫なメンバ関数もあるし、そうでないものもある
    ・CListCtrl のデストラクタが呼ばれる前に m_hWnd をクリアしておくことは必須

    といったところでしょうか。

    上記了解しました。

    今回みたいなケースというのは、そもそも、あまりないんでしょうね。(SysListView32を拡張したい場合は、当然CListCtrlクラスを継承するべきでしょうし・・・。)
    質問が突飛すぎたせいで、色々な方に誤解を招いてしまったみたいです・・・。

    上記を踏まえて、もう少し色々悩んでみます。

    納得の行くアドバイスを頂けましたので、以上でスレを閉じさせていただきます。
    皆さん、有難うございました。

    2009年6月9日 15:19