tags:

views:

191

answers:

2

I have a WTL 8.0 SDI application for Windows Mobile 5. In this contrived example below, I create a view, destroy it, then re-create it. But, when it's re-created assertions in the WM_INITDIALOG handler fail because the control's HWND isn't valid.

I note that I can fix this by handling WM_DESTROY in CMyView and manually destroying every child control. But, I didn't think I should have to. MSDN even says:

This message is sent first to the window being destroyed and then to the child windows (if any) as they are destroyed.

Anybody have an idea as to what's going on?

Edit: If I handle WM_NCDESTROY in CMyView, all of the the child control handles are still valid! (some_control_.IsWindow()==TRUE) That's not how it's supposed to be...

Thanks, 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
A: 

I'm not a hundred percent sure, but it seems like the some_control_ CEdit control is not registered with the parent window. I think you will need to call some_control_.Create(...) with the parent handle as a parameter.

see msdn article for a documentation of CEdit::Create().

Holger Kretzschmar
That's done by the part I have <snipped> as "Message Map and other standard WTL macros". It was intended to include the DDX_MAP which would handle that.
PaulH
A: 

It is not good practice to Create, Destroy and re-Create the same window, you should consider hiding it and reinitializing your contents when showing it again.

Anyhow your code will not ASSERT at re-creation with:

virtual void CMyView::OnFinalMessage(HWND)
{
    some_control_.m_hWnd = 0;
}

cheers, AR

Alain Rist
Thanks for your help. What makes that "not good practice"?
PaulH
Fundamentally because creating and destroying a window is a system demanding operation. Practically because ATL::CWindowImpl does not correctly handle it, so we have extra work :-)
Alain Rist