default WM_DESTROY not properly cleaning up child windows - c++

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

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;
}

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().

Related

Safe place for an ATL object to release itself

Is there a strategy for safely allowing an ATL object to release itself in response to a Windows message or sink event?
In other words, let's say you have an ATL class that's subclassing some windows (using a message map) and/or sinking events from COM objects (using a sink map). And you'd like the class to release itself given a particular message or event. For example, you might want to release when a particular subclassed window receives WM_CLOSE, or you're sinking DWebBrowserEvents2 and want to release upon DISPID_ONQUIT.
I think the problem is that if you release in the middle of your message or event handler, the ATL framework might still have some processing to do afterward (even if you, say, do something like bHandled = TRUE). And if your object has been released/deleted at that point, then bad things will ensue.
Does anyone know of an approach to solve this? Thanks for any input.
you can do next implementation
class MySubClass : public CWindowImplBaseT<>
{
ULONG dwRefCount = 1;
BEGIN_MSG_MAP(MySubClass)
MESSAGE_HANDLER(WM_CHAR, OnChar)
END_MSG_MAP()
LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
bHandled = FALSE;
// for instance unsublass when ESC pressed
// but possible do this on any event
if (uMsg == WM_CHAR && wParam == VK_ESCAPE)
{
UnsubclassWindow(FALSE);
}
return 0;
}
virtual void OnFinalMessage(_In_ HWND hWnd)
{
__super::OnFinalMessage(hWnd);
Release();
}
~MySubClass()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
public:
BOOL SubclassWindow(_In_ HWND hWnd)
{
if (__super::SubclassWindow(hWnd))
{
AddRef();
return TRUE;
}
return FALSE;
}
HWND UnsubclassWindow(_In_ BOOL bForce /*= FALSE*/)
{
if (HWND hwnd = __super::UnsubclassWindow(bForce))
{
// mark window as destroyed
m_dwState |= WINSTATE_DESTROYED;
m_hWnd = hwnd;
return hwnd;
}
return 0;
}
void AddRef()
{
dwRefCount++;
}
void Release()
{
if (!--dwRefCount)
{
delete this;
}
}
};
void DoSubclass(HWND hwnd)
{
if (MySubClass* pObj = new MySubClass)
{
pObj->SubclassWindow(hwnd);
pObj->Release();
}
}
so key points - have reference count on object ( ULONG dwRefCount - here i assume that object will be accessed only from single UI thread, otherwise need use atomic/interlocked operations). delete object inside Release() when dwRefCount became 0. when we subclass window - add additional reference, and in UnsubclassWindow - set WINSTATE_DESTROYED bit in state ( m_dwState |= WINSTATE_DESTROYED ) - as result OnFinalMessage will be called after last message and here we already call Release. or if we not direct unsubclass until window will be destroyed - atl implementation anyway finally call OnFinalMessage where we release self.
According to the documentation for CWindowImpl::OnFinalMessage:
The default implementation of OnFinalMessage does nothing, but you can override this function to handle cleanup before destroying a window. If you want to automatically delete your object upon the window destruction, you can call delete this; in this function.
So that looks like the place to do it.

Wrong parent HWND in modal dialog

Why do i get desktop as a parent HWND for my modal dialog here?
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
HWND parent = GetParent(); // or GetAncestor(m_hWnd, GA_PARENT);
assert(parent != GetDesktopWindow()); // not ok
...
}
....
}
//somewhere in code
//m_hWnd is some valid HWND
assert(m_hWnd != GetDesktopWindow()); //ok
CSaveProfileAsDlg dlg;
dlg.DoModal(m_hWnd /*as a parent wnd*/);
I can "solve" it by passing corret HWND in CSaveProfileAsDlg ctor, but i'd like to have correct solution.
Thank you!
The documentation is very confusing but I think I found the problem. DoModal internally calls ::DialogBox(), one parameter of which takes a HWND named hWndParent. From the documentation:
hWndParent [in, optional]
Type: HWND
A handle to the window that owns the dialog box.
The keyword here is the word "owns". The section about owned windows confirms this:
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.
So we actually talk about the owner window instead of its parent. This makes sense as the dialog is a free floating window and not part of a window hierarchy as "parenthood" would imply.
You can get the owning window by using:
HWND parent = ::GetWindow(m_hWnd, GW_OWNER);
I had a similiar issue. I was wondering why GetParent() return always a different CWnd*.
The right solution is simple, just pass the desired pWnd to the dlg Constructor.
It will be stored in the CDialog member variable m_pParentWnd.
Then you can always get the passed pWnd with this member variable in your Dialog.
//somewhere in code
//pWnd some valid CWnd pointer
CSaveProfileAsDlg dlg (pWnd); // relevant!
dlg.DoModal();
.
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
CWnd* _pWnd = GetParent(); // not ok, returns probably CMainFrame or similiar
CWnd* pWnd = m_pParentWnd; // your parent Wnd
...
}
....
}
I've got two versions of the same application and one of them disables all parent popup windows as it should when DoModal is called. Second version disables only top-level CMainFrame and real parent is stay enabled so I can call modal dialog twice and more.
This happen in CWnd::GetSafeOwner_, line :
hWnd = ::GetLastActivePopup(hWnd);
Firsts version return real parent while second version return CMainFrame.
I've spent one day and could not find the cause of such behaviour. However I've found workaround:
When DoModal is called it Disable CMainFrame so I can disable it's children also:
afx_msg void OnEnable(BOOL bEnable);
ON_WM_ENABLE()
void CMainFrame::OnEnable(BOOL bEnable)
{
std::for_each(m_bars.begin(), m_bars.end(),
[=](const std::pair<EBar, BaseBar*>& bar)
{
bar.second->EnableWindow(bEnable);
});
}

CWinThread message maps

So I'm delving into the world of MFC and in particular, customised use of CWinThread to implement worker threads. I have successfully implemented worker threads other ways, so the main motivation for using CWinThread for a worker thread is to make use of the message map.
For future use I am deriving CWinThread into what will eventually become some form of super class.. See the following declaration..
class WinThreadBase : public CWinThread
{
DECLARE_DYNCREATE(WinThreadBase)
protected:
WinThreadBase(); // protected constructor used by dynamic creation
DECLARE_MESSAGE_MAP()
public:
BOOL isdone_;
};
This is extended and implement by the following
The declaration
class WinThreadImplementation :public WinThreadBase {
DECLARE_DYNCREATE(WinThreadImplementation)
protected:
WinThreadImplementation(); //Declare protected because of dynamic creation
public:
//The following CWinThread methods have to be overriden to complete specfic work.
virtual BOOL InitInstance();
virtual int Run();
private:
CDialog* owner_;
BOOL isinit_;
public:
virtual ~WinThreadImplementation();
void SetOwner( CDialog* pOwner ) { owner_ = pOwner; }
BOOL Isinit() const { return isinit_; }
DECLARE_MESSAGE_MAP()
//Message handler declaration
void OnMyThreadMessage( WPARAM wParam, LPARAM lParam );
void OnQuit( WPARAM wParam, LPARAM lParam );
};
The implemetation
IMPLEMENT_DYNCREATE( WinThreadImplementation, WinThreadBase)
WinThreadImplementation::WinThreadImplementation() {
owner_ = FALSE;
isinit_ = FALSE;
}
WinThreadImplementation::~WinThreadImplementation() {
//........Do some stuff here... //
}
BOOL WinThreadImplementation::InitInstance() {
//Do some initialisation here..
return TRUE; //returning true allows thread start successfully
}
int WinThreadImplementation::Run() {
isinit_ = TRUE;
while( !isdone_ ) {
//Do some work...
//TRACE( "Hello from pat's derived CWinThread" );
Sleep( 1000 ); //Give other threads a chance to run..
}
owner_->PostMessage( WM_QUIT, 0, 0 ); // Tell our parent thread that this thread has finished work
return 0;
}
BEGIN_MESSAGE_MAP(WinThreadImplementation,CWinThread)
//Map messages to handler method here...
//CWinThread messages must be handles like this....
ON_THREAD_MESSAGE(WM_MYTHREADMESSAGE,OnMyThreadMessage)
END_MESSAGE_MAP()
//Put message handlers here...`
void WinThreadImplementation::OnMyThreadMessage( WPARAM wParam, LPARAM lParam ) {
TRACE( "Hello from my message handler\n\r" );
}
Now for actually posting the message
mywinthreadimpl_ = (WinThreadImplementation*)
AfxBeginThread( RUNTIME_CLASS( WinThreadImplementation ), THREAD_PRIORITY_NORMAL,
0, CREATE_SUSPENDED );
mywinthreadimpl_->SetOwner( this );
mywinthreadimpl_->ResumeThread();
while( !mywinthreadimpl_->Isinit() ); //Make sure that the thread has initialised before attempting to post a message
if( PostThreadMessage( mywinthreadimpl_->m_nThreadID, WM_MYTHREADMESSAGE, NULL, NULL ) ) {
TRACE( "message was sent correctly\n" );
}
So the result of this is that it compiles, my CWinThread derivation is working and enters the overriden run function. But I cannot for the life of me receive the message posted by PostThreadMessage.
I have read the following
http://support.microsoft.com/kb/142415?wa=wsignin1.0
and concluded that this does not apply to me since I am using VS 2010.
Can anyone suggest what I may have missed, that would be preventing my CWinThread implementation from receiving the messages?
Thanks
Without using AfxPumpMessage or calling the base class implementation of CWinThread (__super::Run), you will never receive a message!
Don't use isdone_. Instead use PostQuitMessage to terminate thecurrent workerthread too. Just use the base implementation of Run to run the thread and pump the messages.
You can also use OnIdle or other function of CWinThread, to do some work...
Just calling Sleep blocks your thread but the thread isnever interrupted by a windows message

MFC - Cannot Post Message to a custom class derived from CWnd

I have a custom class derived from CWnd that I would like to post a message to from a worker thread. I am using the PostMessage function to achieve this. The first argument to PostMessage is the HWND type handle to my class, and the next is the message I would like handled. For the first parameter, I generate the handle to my class using GetSafeHwnd() function, and for the second parameter, I use WM_USER+3. Also, I declare a message map inside my class header file, and add an entry for the message handler inside the BEGIN_MESSAGE_MAP and END_MESSAGE_MAP block. However, my handler is not getting called. I have also checked the return value of PostMessage function, it is 1, that means success.
Here is my code :
Inside MyClass.h
class CMyClass : CWnd
{
....
....
public:
void InitHandle();
protected:
afx_msg LRESULT OnMessageReceived(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
}
Inside MyClass.cpp
#define WM_MY_MESSAGE WM_USER+3
HWND handleToMyClassWindow;
BEGIN_MESSAGE_MAP(CMyClass, CWnd)
ON_MESSAGE(WM_MY_MESSAGE, OnMessageReceived)
END_MESSAGE_MAP()
LRESULT CMyClass::OnMessageReceived(WPARAM wParam, LPARAM lParam)
{ .... }
void CMyClass::InitHandle()
{
handleToMyClassWindow = GetSafeHwnd();
}
Inside Worker thread
UINT WorkerThreadFunction(LPVOID pParam )
{
....
PostMessage(handleToMyClassWindow, WM_MY_MESSAGE, NULL, NULL);
....
}
My question is, what are the possible reasons for the OnMessageReceived handler to not be called.
P.S.
I take care that the calling object calls the InitHandle() function.
I tried the same technique with the View class (derived from CView) of my program, and it works there, but fails here.
You cannot post to a window if it has not been created. GetSafeHwnd() will return NULL if you have not actually created a window using your class.

Can't get the handle of a Cwnd Class in MFC Windowless Activex?

I have asked two questions earlier about this and for each post there was some solutions i tried them, but the problem still exist.
My first question was : why a windowless Activex does not return the Handle. the suggestion was "change the creation setting an make windowless activate off, i have tried it but still m_hWnd property has returned zero as GetSafeHwnd() method has did.
the second one was the same question this one focused on COleControl class and it's ancestor CWnd. the solution was as this "Create invisible window somewhere in your control initialization code. Handle the messages sent to this window, and call controls methods directly". so i did that but the created class still returns zero handle.
here is my new invisible class source:
// moWind.cpp : implementation file
//
#include "stdafx.h"
#include "PINActive.h"
#include "moWind.h"
#include "include\xfspin.h"
#include <math.h>
// moWind
IMPLEMENT_DYNAMIC(moWind, CWnd)
moWind::moWind(){}
moWind::~moWind(){}
//=============================================================
LRESULT moWind::OnExecuteEvent (WPARAM wParam, LPARAM lParam)
{
WFSRESULT *pResult = (WFSRESULT *)lParam;
CString EK=_T("");
CString str;
int reskey=0;
if (pResult->u.dwEventID=WFS_EXEE_PIN_KEY)
{
LPWFSPINKEY pressedkey;
pressedkey=(LPWFSPINKEY)pResult->lpBuffer;
reskey = log10((double)pressedkey->ulDigit) / log10((double)2);
EK.Format("%d",reskey);
xfsOnKeyEvent->OnKeyRecieved(reskey);
}
else
{
str.Format("ExecuteEvent: ID = %d\r\n", pResult->u.dwEventID);
}
MessageBox("a Execute message Recieved");
return 0;
}
BEGIN_MESSAGE_MAP(moWind, CWnd)
ON_MESSAGE(WFS_EXECUTE_EVENT,OnExecuteEvent)
END_MESSAGE_MAP()
and this is .h file of the class:
// moWind.h
class IXFSEvents
{
protected:
IXFSEvents(){};
virtual ~IXFSEvents(){};
public:
virtual void OnKeyRecieved(int key)=0;
};
class moWind : public CWnd
{
DECLARE_DYNAMIC(moWind)
public:
moWind();
virtual ~moWind();
void Register(IXFSEvents* obj)
{
xfsOnKeyEvent= obj;
}
protected:
IXFSEvents* xfsOnKeyEvent;
LRESULT OnExecuteEvent (WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
and at the end here this the way I've used this class in my Activex:
in the myActivex.h file:
include "moWind.h"
class CmyActivexCtrl : public COleControl, public IXFSEvents
{
...
Class definition
...
protected:
moWind tmpWind;
.
.
};
finally in the creation method of myActivex i have initialized the component callback method an wanted to get it's Handle as this:
CmyActivexCtrl::CmyActivexCtrl()
{
InitializeIIDs(&IID_DmyActivex, &IID_DmyActivexEvents);
tmpWind.Register(this);
myOtherComponent.WindowsHandle=tmpWind.GetSafeHwnd(); //here my Cwnd derived class returns zero
//my other component gets the handle and call an API with it to register
//the given handle and force the API to send the messages to that handle.
}
As you mentioned you need a window handle to be able to receive user messages through it, you always have an option of creating a helper window, such as message only window, see Using CreateWindowEx to Make a Message-Only Window.
For your windowless control it is okay to not have any window handle at all, so you cannot really rely on handle availability unless you own a window yourself.