eelko
asked on
Problem PostMessage in CDialog::InitDialog()
In a dialog based application I have two dialogs which I call form CMyApp::InitInstance().
Like:
CFirstDlg dlg1;
dlg1.DoModal();
CSecondDlg dlg2;
dlg2.DoModal();
To start an automatic process (like searching files on a hard disk)I post a user message from within CFirstDlg::InitDialog()
When I run the release build .exe I get an Assertion Failure within MFC-code. Though, when building and running the debug .exe it all works fine!
I already know that the problem is the PostMessage cause when I remove it, no assertion failure occures.
I guess a solution could be to build a modeless dialog but are there other solutions which will keep using DoModal?
Eelko.
Like:
CFirstDlg dlg1;
dlg1.DoModal();
CSecondDlg dlg2;
dlg2.DoModal();
To start an automatic process (like searching files on a hard disk)I post a user message from within CFirstDlg::InitDialog()
When I run the release build .exe I get an Assertion Failure within MFC-code. Though, when building and running the debug .exe it all works fine!
I already know that the problem is the PostMessage cause when I remove it, no assertion failure occures.
I guess a solution could be to build a modeless dialog but are there other solutions which will keep using DoModal?
Eelko.
Post a message to what window ? Be more specific.
ASKER
Post a message to myself, e.g.:
BEGIN_MESSAGE_MAP(CFileSea rcherDlg, CDialog)
ON_MESSAGE(UM_STARTSEARCH, OnStartSearch)
END_MESSAGE_MAP()
BOOL CFirstDlg::InitDialog()
{
CDialog::OnInitDialog();
PostMessage( UM_STARTSEARCH );
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
BEGIN_MESSAGE_MAP(CFileSea
ON_MESSAGE(UM_STARTSEARCH,
END_MESSAGE_MAP()
BOOL CFirstDlg::InitDialog()
{
CDialog::OnInitDialog();
PostMessage( UM_STARTSEARCH );
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
Try this code on your init dialog:
ShowWindow( SW_SHOW);
RedrawWindow();
OnStartSearch();
ShowWindow( SW_SHOW);
RedrawWindow();
OnStartSearch();
ASKER
No change, though, I now see the window before the error appears.
In OnStartSearch I post again a message (to myself) and probably the program now fails during this post.
I found out that the problem lies within CWnd::GetDlgItem() (in winocc.cpp). In debug probably m_pCtrlCont == NULL since all data in debug mode is initialized to 0.
In release mode m_pCtrlCont is invalid and m_pCtrlCont->GetDlgItem(nI D, phWnd); fails.
void CWnd::GetDlgItem(int nID, HWND* phWnd) const
{
ASSERT(::IsWindow(m_hWnd)) ;
ASSERT(phWnd != NULL);
if (m_pCtrlCont == NULL)
*phWnd = ::GetDlgItem(m_hWnd, nID);
else
m_pCtrlCont->GetDlgItem(nI D, phWnd);
}
In OnStartSearch I post again a message (to myself) and probably the program now fails during this post.
I found out that the problem lies within CWnd::GetDlgItem() (in winocc.cpp). In debug probably m_pCtrlCont == NULL since all data in debug mode is initialized to 0.
In release mode m_pCtrlCont is invalid and m_pCtrlCont->GetDlgItem(nI
void CWnd::GetDlgItem(int nID, HWND* phWnd) const
{
ASSERT(::IsWindow(m_hWnd))
ASSERT(phWnd != NULL);
if (m_pCtrlCont == NULL)
*phWnd = ::GetDlgItem(m_hWnd, nID);
else
m_pCtrlCont->GetDlgItem(nI
}
Can you post the whole code for CFirstDlg ? I want to debug it myself.
Why are u trying to post messages from within the dialog when u can invoke methods ?
Why are u trying to post messages from within the dialog when u can invoke methods ?
ASKER
The FirstDlg.cpp file:
#define UM_STARTSEARCH WM_APP + 1
#define UM_SEARCHNEXT WM_APP + 2
#define UM_SEARCHDONE WM_APP + 3
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CFirstDlg dialog
CFirstDlg::CFirstDlg(CWnd* pParent /*=NULL*/)
: CDialog(CFirstDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CFirstDl g)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_pSearcher = NULL;
}
void CFirstDlg::DoDataExchange( CDataExcha nge* pDX)
{
CDialog::DoDataExchange(pD X);
//{{AFX_DATA_MAP(CFirstDlg )
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CFirstDl g, CDialog)
//{{AFX_MSG_MAP(CFirstDlg)
//}}AFX_MSG_MAP
ON_MESSAGE(UM_STARTSEARCH, OnStartSearch)
ON_MESSAGE(UM_SEARCHNEXT, OnSearchNext)
ON_MESSAGE(UM_SEARCHDONE, OnSearchDone)
END_MESSAGE_MAP()
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CFirstDlg message handlers
BOOL CFirstDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_pSearcher = new CSearcher(this);
PostMessage(UM_STARTSEARCH );
return TRUE;
}
void CFirstDlg::OnCancel()
{
if( m_pSearcher )
m_pSearcher->m_bStopSearch = TRUE;
PostMessage(UM_SEARCHDONE, FALSE);
}
LRESULT CFirstDlg::OnStartSearch()
{
m_pSearcher-> SetFileToSearch("Notepad.e xe");
PostMessage(UM_SEARCHNEXT) ;
return 0;
}
LRESULT CFirstDlg::OnSearchNext()
{
if( m_pSearcher-> Search() )
PostMessage(UM_SEARCHDONE, TRUE);
return 0;
}
LRESULT CFirstDlg::OnSearchDone(BO OL bResult)
{
delete m_pSearcher;
if( bResult )
CDialog::OnOK();
else
CDialog::OnCancel();
return 0;
}
The CSearcher object does the real work. It scans all drives and its directories for the specified file(s). The object builds up a list with directories to search (initially only the root directory of each drive). The Search method will search the first directory of this list, add names of the subdirectories to the list and sends the UM_SEARCHNEXT message to the dialog. It is done when the list is empty.
The reason why I use so much posting of messages is that if I don't the user will not be able to press the "Cancel"-button since this event is handled after the method is completed and the dialog gets back to its message loop.
But if you know a better way to do this, I will be happy to hear it.
#define UM_STARTSEARCH WM_APP + 1
#define UM_SEARCHNEXT WM_APP + 2
#define UM_SEARCHDONE WM_APP + 3
//////////////////////////
// CFirstDlg dialog
CFirstDlg::CFirstDlg(CWnd*
: CDialog(CFirstDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CFirstDl
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_pSearcher = NULL;
}
void CFirstDlg::DoDataExchange(
{
CDialog::DoDataExchange(pD
//{{AFX_DATA_MAP(CFirstDlg
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CFirstDl
//{{AFX_MSG_MAP(CFirstDlg)
//}}AFX_MSG_MAP
ON_MESSAGE(UM_STARTSEARCH,
ON_MESSAGE(UM_SEARCHNEXT, OnSearchNext)
ON_MESSAGE(UM_SEARCHDONE, OnSearchDone)
END_MESSAGE_MAP()
//////////////////////////
// CFirstDlg message handlers
BOOL CFirstDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_pSearcher = new CSearcher(this);
PostMessage(UM_STARTSEARCH
return TRUE;
}
void CFirstDlg::OnCancel()
{
if( m_pSearcher )
m_pSearcher->m_bStopSearch
PostMessage(UM_SEARCHDONE,
}
LRESULT CFirstDlg::OnStartSearch()
{
m_pSearcher-> SetFileToSearch("Notepad.e
PostMessage(UM_SEARCHNEXT)
return 0;
}
LRESULT CFirstDlg::OnSearchNext()
{
if( m_pSearcher-> Search() )
PostMessage(UM_SEARCHDONE,
return 0;
}
LRESULT CFirstDlg::OnSearchDone(BO
{
delete m_pSearcher;
if( bResult )
CDialog::OnOK();
else
CDialog::OnCancel();
return 0;
}
The CSearcher object does the real work. It scans all drives and its directories for the specified file(s). The object builds up a list with directories to search (initially only the root directory of each drive). The Search method will search the first directory of this list, add names of the subdirectories to the list and sends the UM_SEARCHNEXT message to the dialog. It is done when the list is empty.
The reason why I use so much posting of messages is that if I don't the user will not be able to press the "Cancel"-button since this event is handled after the method is completed and the dialog gets back to its message loop.
But if you know a better way to do this, I will be happy to hear it.
First for a window u can use WM_USER + X message.
Second, the search operation is done in OnSearchNext, the OnSearchDone and OnStartSearch are unsignifiant form the point of execution time so the app will hang until the OnSearchNext execution is completed. So that PostMessage thing isn't helping at all.
The solution will be to put the search process in a separate thread, a working thread that will do all the work in background. Look at AfxBeginThread function ( first form )to learn how to work with working threads. The AfxBeginThread returns a pointer to a CWinThread class that u can use it to stop the search when user press the cancel. U can pass to the thread proc a pointer to your window to update a progress bar or somenthing else to show the progress of search to the user ( if u want).
A little example:
UINT thread_proc( LPVOID pParam )
{
m_pSearcher-> SetFileToSearch("Notepad.e xe");
if ( m_pSearcher-> Search() )
(CFirstDlg*)pParam->OnOk() ;
else
(CFirstDlg*)pParam->OnCanc el();
}
BOOL CFirstDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxBeginThread( thread_proc, this );
return TRUE;
}
Second, the search operation is done in OnSearchNext, the OnSearchDone and OnStartSearch are unsignifiant form the point of execution time so the app will hang until the OnSearchNext execution is completed. So that PostMessage thing isn't helping at all.
The solution will be to put the search process in a separate thread, a working thread that will do all the work in background. Look at AfxBeginThread function ( first form )to learn how to work with working threads. The AfxBeginThread returns a pointer to a CWinThread class that u can use it to stop the search when user press the cancel. U can pass to the thread proc a pointer to your window to update a progress bar or somenthing else to show the progress of search to the user ( if u want).
A little example:
UINT thread_proc( LPVOID pParam )
{
m_pSearcher-> SetFileToSearch("Notepad.e
if ( m_pSearcher-> Search() )
(CFirstDlg*)pParam->OnOk()
else
(CFirstDlg*)pParam->OnCanc
}
BOOL CFirstDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxBeginThread( thread_proc, this );
return TRUE;
}
ASKER
I modified the code like in your sample but now the searcher object does nothing and form the Dialog object I can't access the searcher object to stop the search. Somewhere in MSDN I read that I cannot use MFC objects in two different threads. It seems to me that this is a lot of extra work for something that already works in debug mode.
Hi,
We face no problems in debug mode exe but do face assertion failure in release mode exe.
Comming to the problem,
To get a solution without using modeless dlg box shall be,
Declare application document template and initialize it.
Then declare the document pointer and initialise it using the document template pointer member function.
Once the document pointer is obtained, it is easy to obtain its view.
Then call PostMessage function on the view window.
regards
sun
We face no problems in debug mode exe but do face assertion failure in release mode exe.
Comming to the problem,
To get a solution without using modeless dlg box shall be,
Declare application document template and initialize it.
Then declare the document pointer and initialise it using the document template pointer member function.
Once the document pointer is obtained, it is easy to obtain its view.
Then call PostMessage function on the view window.
regards
sun
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
In the code that I post it's intended to work with the dialog using the pointer passed in pParam. The search object need to exist only in the thread. To stop the thread use a CWinThread * pWinThread = AfxBeginThread( thread_proc, this ) object and use the CWinThread::SuspendThread and CWinThread::ResumeThread to pause and resume the search. Use a var in your dialg to pass the search object the file name. You can also use a var in your dialog( that the thread_proc checks periodicaly ) to cancel the execution of the thread.
Good luck.
Good luck.
What kind of object is CSearcher ?
ASKER
Excellent! I fixed it and now it works perfect.
Thanks, Eelko.
Thanks, Eelko.
No problem ... you're welcome.
have a nice day,
regards,
ZOPPO
have a nice day,
regards,
ZOPPO