по умолчанию WM_DESTROY не очищает дочерние окна должным образом

У меня есть SDI-приложение WTL 8.0 для Windows Mobile 5. В этом надуманном примере ниже я создаю представление, уничтожаю его, а затем заново создаю. Но при повторном создании утверждений в обработчике WM_INITDIALOG происходит сбой, потому что HWND элемента управления недействителен.

Замечу, что я могу исправить это, обработав WM_DESTROY в CMyView и вручную уничтожив каждый дочерний элемент управления. Но я не думал, что должен. MSDN даже говорит:

Это сообщение сначала отправляется разрушаемому окну, а затем дочерним окнам (если таковые имеются) по мере их уничтожения.

Кто-нибудь имеет представление о том, что происходит?

Изменить: если я обрабатываю WM_NCDESTROY в CMyView, все дескрипторы дочерних элементов управления по-прежнему действительны! (some_control_.IsWindow()==TRUE) Это не так, как должно быть ...

Спасибо, PaulH

class CMyView : public CDialogImpl< CMyView >,
                public CWinDataExchange< CMyView >
{
    // <snip> Message Map and other standard WTL macros </snip>

    LRESULT OnInitDialog( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
    {
        DoDataExchange( FALSE );
        // assertion fails within the SetWindowText() call
        // atlwin.h line 876
        // ATLASSERT(::IsWindow(m_hWnd));
        some_control_.SetWindowText( _T( "Foo" ) );
        return 0;
    };

private:
    CEdit some_control_;
}; // class CMyView

class CMainFrame : public CFrameWindowImpl< CMainFrame >, 
                   public CUpdateUI< CMainFrame >,
                   public CMessageFilter, 
                   public CIdleHandler
{
public:
    // <snip> Message Map and other standard WTL macros </snip>

    BOOL CMainFrame::PreTranslateMessage( MSG* pMsg )
    {
        if( CFrameWindowImpl< CMainFrame >::PreTranslateMessage( pMsg ) )
            return TRUE;

        return my_view_.PreTranslateMessage( pMsg );
    };

    LRESULT OnCreate( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
    {
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        ATLASSERT( pLoop != NULL );
        pLoop->AddMessageFilter( this );
        pLoop->AddIdleHandler( this );

        m_hWndClient = my_view_.Create( m_hWnd );
        my_view_.DestroyWindow();
        m_hWndClient = my_view_.Create( m_hWnd );
    };

private:
    CMyView my_view_;
}; // class CMainFrame

person PaulH    schedule 11.11.2009    source источник


Ответы (2)


Создавать, уничтожать и повторно создавать одно и то же окно - не лучшая практика, вам следует подумать о том, чтобы скрыть его и повторно инициализировать свое содержимое при его повторном отображении.

В любом случае ваш код не будет АССЕРТИРОВАТЬ при повторном создании с:

virtual void CMyView::OnFinalMessage(HWND)
{
    some_control_.m_hWnd = 0;
}
person Alain Rist    schedule 06.03.2010
comment
В основном потому, что создание и уничтожение окна - это операция, требующая больших усилий. Практически потому, что ATL :: CWindowImpl неправильно обрабатывает его, поэтому у нас есть дополнительная работа :-) - person Alain Rist; 09.03.2010

Я не уверен на сто процентов, но похоже, что элемент управления some_control_ CEdit не зарегистрирован в родительском окне. Я думаю, вам нужно будет вызвать some_control_.Create (...) с родительским дескриптором в качестве параметра.

документацию по CEdit :: Create ( ).

person codencandy    schedule 09.12.2009
comment
Это делается с помощью части, которая у меня ‹snipped› в качестве карты сообщений и других стандартных макросов WTL. Он был предназначен для включения DDX_MAP, который справился бы с этим. - person PaulH; 18.12.2009