Solved

CDialog::DoModal doesn't show the window

Posted on 1998-07-27
27
3,903 Views
Last Modified: 2008-02-01
When calling DoModal of a CDialog derived object (an error message dialog),
it is not shown on the screen; the event OnShowWindow (WM_SHOWWINDOW) is not even reached.

The parent window is a CFrameWnd derived class having the attribute "DS_SYSMODAL",
and having one child window displayed.

When this child window is not yet created, the error message dialog is displayed;
but after creation of the child the error box isn't shown any more.

Even when the child window is parent of the error dialog, the error is invisible.

The error message comes out of a worker thread.
0
Comment
Question by:PC-Alex
  • 9
  • 7
  • 4
  • +3
27 Comments
 
LVL 22

Expert Comment

by:nietod
Comment Utility
can you post some relevant code?
0
 
LVL 2

Expert Comment

by:rayb
Comment Utility
Do you have any incorrectly or unregistered ActiveX controls embedded on this dialog?  These errors will usually fail silently.
0
 
LVL 7

Expert Comment

by:psdavis
Comment Utility
I agree with rayb.  

If you don't have your ActiveX control registered or you are missing a DLL that your ActiveX relies on, everything will start to initialize until the DoModal executes.  It will just return silently in the OnInitDialog.  If your dialog executes OnInitDialog but nothing else, look for it!

Phillip
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
rayb and psdavis,
I don't use any ActiveX in my program at all, absolutely sure.
The error dialog contains one BMP and one static control.

nietod,
you want to see code ? OK, the CMainFrame is made like this:
CMainFrame * pMainFrame = new CMainFrame;
      
if (!pMainFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | WS_VISIBLE | DS_SYSMODAL))
{
  ErrorHandling();
}

. and the error dialog is just created in the resource editor, ClassWizard made a class of it and that was it. Do you want the code of the .rc file ?

0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
How about the code that creates and shows the dialog?  or the code that creates the child window.

I don't use MFC, but the documentation I can find for LoadFrame() says that the styles are passed as the second parameter, but you are passing them as the first.  Is that a mistake or is this a different LoadFrame()?
0
 
LVL 7

Expert Comment

by:psdavis
Comment Utility
Post the full CDialog class source code.  Also post the CMainFrame function that creates the dialog (look for DoModal).

It will be something like

CMyDialog pMyDialog;
pMyDialog.DoModal( );

I only do MFC.  You'll get both points of view!

Phillip

0
 
LVL 7

Expert Comment

by:psdavis
Comment Utility
How about...

DS_SYSMODAL on a CMainFrame?

DS_SYSMODAL
 Creates a system-modal dialog box. This style causes the dialog box to have the WS_EX_TOPMOST style, but otherwise has no effect on the dialog box or the behavior of other windows in the system when the dialog box is displayed.
 
See if you can take the DS_SYSMODAL off of the CMainFrame and only put it on the dialog!  

Phillip

0
 
LVL 7

Expert Comment

by:psdavis
Comment Utility
Actually, the DS_SYSMODAL doesn't even do anything anymore.  That was Win3.1

I say, remove it for now and see if everything else works as normal.  You can always use a SetWindowPos to set the topmost style instead.

Phillip
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
Removing DS_SYSMODAL has a pretty feasable effect; you can now fetch windows from the background above the FrameWnd (like a notepad, explorer etc).

When using DS_SYSMODAL, this is impossible, at least in a RELEASE Build (I wonder why in DEBUG mode this doesn't hold true, ... but that's not the problem).

nietod, my documentation about CFrameWnd::LoadFrame says:
virtual BOOL LoadFrame( UINT nIDResource, DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, CWnd* pParentWnd = NULL, CCreateContext* pContext = NULL );

One interesting point is that CriticalErrorDialog's hWnd is NULL between the constructor and the call to DoModal(). Is this alright ?

psdavis, removing DS_SYSMODAL out of the FrameWnd didn't change anything. Patching it into the RC file to the attributes of the IDD_DIALOG_CRITERROR also didn't change anything.
 
Finally some further code for those who asked:

//////////////////////////////////////////////////////////////////////////////////
// ErrorHandler.cpp:
void CErrorHandler::DisplayCriticalError(CString ErrorMessage)
{
  // here is the called CDialog that is not shown
  CErrorCritical CriticalErrorDialog(m_pParentWnd, ErrorMessage);
  CriticalErrorDialog.DoModal();
}

CErrorCritical::CErrorCritical(CWnd* pParent, CString strMessage)
      : CDialog(CErrorCritical::IDD, pParent)
{
  m_strMessage = strMessage;
}

void CErrorCritical::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(CErrorCritical)
      DDX_Text(pDX, IDC_STATIC_MESSAGSE, m_strMessage);
      //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CErrorCritical, CDialog)
      //{{AFX_MSG_MAP(CErrorCritical)
      ON_WM_SHOWWINDOW()
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CErrorCritical::OnShowWindow(BOOL bShow, UINT nStatus)
{
  CDialog::OnShowWindow(bShow, nStatus);  // is never reached !!
}

//////////////////////////////////////////////////////////////////////////////////
// ErrorHandler.h:
class CErrorCritical : public CDialog
{
// Construction
public:
      CErrorCritical(CWnd* pParent = NULL, CString strMessage = "");   // standard constructor

// Dialog Data
      //{{AFX_DATA(CErrorCritical)
      enum { IDD = IDD_DIALOG_CRITERROR };
      CString      m_strMessage;
      //}}AFX_DATA


// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CErrorCritical)
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL

// Implementation
protected:

      // Generated message map functions
      //{{AFX_MSG(CErrorCritical)
      afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};
//////////////////////////////////////////////////////////////////////////////////
// resource file:
IDD_DIALOG_CRITERROR DIALOG DISCARDABLE  0, 0, 286, 338
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Fehlerüberwachung"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,229,317,50,14
    CONTROL         141,IDC_STATIC,"Static",SS_BITMAP,7,7,271,273
    LTEXT           "Static",IDC_STATIC_MESSAGSE,7,286,214,45
END


0
 
LVL 6

Expert Comment

by:seippg
Comment Utility
PC-Alex,

Two points:

1.  One thing you said in your original message looks quite interesting.  You mention that the dialog box is created in a worker thread.  You also mention that the frame window is the parent.  Question?  How are you passing the pFrameWnd to the worker thread.  If you are accessing the pointer from the CWinApp then you have a problem.  You cannot access that pointer from a worker thread.  What you have to do is store the hwnd of the pFrameWnd someplace and then, in your worker thread, reinstantiate your frame window around it using FromHandle(HWND).  Like this:

In your main application thread:

CMyApp *pApp = (CMyApp *)AfxGetApp();
pApp->m_hFrame = pApp->m_pMainFrame->GetSafeHwnd();

Then, in your worker thread:

CMyFrame *pFrame = CMyFrame::FromHandle(((CMyApp *)AfxGetApp())->m_hFrame);
CErrorCritical dlg(ID, pFrame);
dlg.DoModal();

I didn't compile that code so you may have to futz with it a bit.  

2.  Make CERTAIN that your worker threads are created through CWinThread.  If they are not you will not be able to access ANY MFC object.  Look at "Multithreading:  Programming tips" for more on that.

Hope it helps.
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
seippg,
the worker thread indeed is CWinThread derived. I tried your code, but that didn't change the behaviour of the program.
When I compare the parent window before and after this change, the m_hWnd is exactly the same.#
As I was told, worker threads don't have local CHandleMaps, only "real" UI-Threads do.

But nevertheless, the idea was good and I think I'm on the right track. Because I found out something that made me think:
I examined the state of the WinApp thread while the worker thread waits for a "OK" click in a window I don't see.
The WinApp thread is positioned at a "WaitForSingleObject(.., INFINITE)" command, waiting for the worker thread to set his "I'm finished!" event.

Could it be that the WinApp is deadlocked in that moment, not processing a WM_PAINT that must be executed in order to display the ErrorDialog ?
(I checked that by changing the "WaitForSingleObject(.., INFINITE)" into "while (WaitForSingleObject(.., 0) != WAIT_OBJECT_0) Sleep(0);", but that didn't change anything)

Is there a possibility to call something like "DoEvents" in VisualBasic, I thought about CWinApp::DefWindowProc or so ?

0
 
LVL 6

Expert Comment

by:seippg
Comment Utility
You can't block your main application thread.  If you want your main thread to be notified that some work is done or that an event has occurred you have to post it a message or put a timer on the main thread and have it periodically test the state of an object or variable (the first method is preferred).  Don't block the thread.  It is likely that this is the core of your problem.  By blocking your thread you have shut down your application's message pump so no painting can occur.  
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
seippg,

your answer is that what I wrote in my last comment, but still that doesn't solve my problem. I repeat:

Is there a possibility to call something like "DoEvents" in VisualBasic, I thought about CWinApp::DefWindowProc or so ?
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 22

Expert Comment

by:nietod
Comment Utility
Rather than using WaitForSingleObject() us MsgWaitForMultipleObjects().  This will involve a little more work, however.
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
I guess you could also use WaitForSingleObject with a short wait time in a loop.  (you are going to need a loop in either case).  In either case, when the wait terminates, if the object has not been signaled, you want to process messages until there are no more messages and then return to waiting.
0
 
LVL 6

Expert Comment

by:seippg
Comment Utility
PC-Alex,

Yes you can but you may not like the result.  You can use a PeekMessage loop and call TranslateMessage and DispatchMessage.  The problem is that, if your application's ordinary message loop does any other processing beyond TranslateMessage and DispatchMessage, that processing will be skipped until your PeekMessage loop terminates.  There is nearly always a better way of handling this situation than this.  Notice that, by creating such a loop, you have almost (though, admitedly, not completely) done away with any of the benifits of multithreading.  PeekMessage loops were the way things were done in Windows 3.1 to keep your application alive while background processing continued.  You will probably find that restructuring your code so that it doesn't require this loop will take a lot less time and be easier to maintain in both the short and long run.
0
 
LVL 1

Expert Comment

by:tulin
Comment Utility
Try do this ( suppose that you are in function of class derived from CWinThread )

 CErrorCritical theDialog;
 m_pMainWnd = &theDialog;
 theDialog.DoModal();
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
tulin,

the problem is, that the function that detects the error and reports it is called as well  from the CWinApp as from the worker thread, so this idea won't help.

nietod,
I also discovered MsgWaitForMultipleObjects() but from the documentation it seemed to me that the message loop is not processed during that. Am I wrong ?
If I'd  insert some dummy floating point calculations into the Wait...Sleep loop, would the message loop get processed then ?

seippg,
on the long run I will restructure my code to clean that situation, but I guess you can imagine that this takes a couple of days and doesn't solve my short term problem.
0
 
LVL 1

Expert Comment

by:tulin
Comment Utility
PC_alex,

There are two types of CWinApp threads: worker threads and user interface threads.
You say that your thread is "worker", but you want to show dialog from him, and it means that your thread must be user-interface. The difference between two types is in how to create them. There are two forms of AfxBeginThread (see help), you must use second one. And, as I have already said, before showing any window from thread you must set m_pMainWnd. If I dont mistake, in CWinThread initially m_pMainWnd pointer is NULL; so you can check for this to not to change m_pMainWnd of CWinApp object. Do like this:
bool bInThread = m_pMainWnd ? false :  { true ;m_pMainWnd = &dlg; }
dlg.DoModal();
if (bInThread) m_pMainWnd = NULL;
As I understood, your function is not class member, so you can pass m_pMainWnd there ( by reference )

0
 
LVL 6

Expert Comment

by:seippg
Comment Utility
PC_Alex,

Well, I had some time so I decided to try to replicate your problem.  I Created an app and gave the main window the DS_SYSMODAL style (somewhat dubious) and created a work thread whenever a new document was created.  In the work thread I brought up a dialog without any problem.  Perhaps you could expand on the problem or create a small simple app and see if you can replicate the problem yourself.
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
Hey guys,
thanks for the continued support ... I'm on weekend at home, but right on Monday I'll check out tulin's proposal. The interesting thing is that I was wrong telling that the worker thread was CWinThread derived ... this special thread isn't, it's not created with "CWinThread::CreateThread()", so this makes me hopeful of getting it done on the "tulin way".
seippg, the App is quite large, I guess 750 kB source code together, and I think it'll be easier to make the big structural change (CWinApp doesn't WAIT, this will be done by a separate thread) than filtering out the parts that are of interest.
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
I converted the worker thread into a real user-interface thread, calling AfxBeginThread with the RUNTIME_CLASS.

That didn't change the problem; I guess I can't avoid wrapping  the WaitForSingleObject part into an own thread.
0
 
LVL 6

Expert Comment

by:seippg
Comment Utility
PC-Alex,

Don't be surprised if your latest idea doesn't fix the problem.  I think it's worth a shot but, like I mentioned in my last response, I was not able to recreate the problem using the design constraints which you mentioned in your original email (i.e. I created an app and it worked just fine).  I've seen this happen before.  Someone, somewhere in the code, fiddles with some handles incorrectly or does something incorrectly with some GDI resources and something completely unrelated breaks or doesn't work.  It may be tough to track down.  Try to resist the urge to strangle the offending coder when you find it. Best of luck.

Cheers.

0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
seippg,

what wouldn't you  say when the Dialog comes up if the CWinApp DOESNT wait, but if it waits for the detecting thread to stop then the dialog doesn't appear ?
That's the fact in the moment, and the only clue to me is that CWinApp mustn't wait.
(Forget DS_SYSMODAL, with or without, its the same)
0
 
LVL 6

Accepted Solution

by:
seippg earned 200 total points
Comment Utility
Oh, okay.  So you did get it to come up if the CWinApp doesn't wait.  Whew!  That's a relief.  No, you are precisely correct then.  The dialog couldn't come up because the thread was blocked.  The thing most people do to wait for a work thread to finish is this:

Create a message and define a message handler in your main window.  This has to be done manually--there is no support for this in the class wizard:

#define WM_MYMESSAGE (WM_USER + 1)
BEGIN_MESSAGE_MAP( CMyWnd, CMyParentWndClass )
    //{{AFX_MSG_MAP( CMyWnd  
    ON_MESSAGE( WM_MYMESSAGE, OnMyMessage )
    // ... Possibly more entries to handle additional messages
    //}}AFX_MSG_MAP
END_MESSAGE_MAP( )

Then create the message handler
1.  Insert a declaration for your message handler in your AFX_MSG macro

      //{{AFX_MSG(CFooMain)
      afx_msg LONG OnMyMessage(UINT ui, LONG lnum);   //<--insert this line in
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()

2. Create the message handler:

LONG CFooMain::OnMyMessage(UINT wparam, LONG lparam)
{
      switch (wparam)
      {
      case ID_ERROR:
            CErrorDlg dlg;
            dlg.DoModal();
            return 0;
      default:
            (CFooApp *)AfxGetApp()->m_WorkFinished = TRUE;
      }
      return 0;
}

Finally, somewhere in your work thread post the message indicating an error code (or lack of error) in your wparam:

    if (bError)
        ((CFooApp *)AfxGetApp())->m_pMainWnd->PostMessage(ID_ERROR);
    else if (bDone)
        ((CFooApp *)AfxGetApp())->m_pMainWnd->PostMessage(ID_NOERROR);

The only thing left to do is make sure that you test the m_bWorkFinished variable in your application where appropriate (i.e. in a cancel dialog) so that you can stop the work thread.  See the help on WM_MESSAGE for more info.  Let me know how it goes.
0
 
LVL 1

Author Comment

by:PC-Alex
Comment Utility
seippg,

the problem is solved; I isolated the task waiting for the worker thread to terminate, wrapped a CWinThread around it and everything is fine. Yout idea with messaging is not applicable for this problem.
 
Nevertheless, you were the first one to lead me to examine the different threads, and you did excellent support, so you're really the one who has earned excellent 200 points.
0
 
LVL 6

Expert Comment

by:seippg
Comment Utility
Glad to hear.  It sounds like you've made some good and needed changes to the app as well.

Cheers.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now