• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1694
  • Last Modified:

Modal window with CreateWindowEx

Hi,

I am working on Win-NT and Win-2K with MS Visual C++ 6, using SDK (i.e. not MFC).

I have a custom window that I create with CreateWindowEx. I want to make this window modal (i.e. not only visible on top, but really modal, with the program waiting for the window to return).

Is there a mean to achieve this ? If you have a solution, please include an example of code.

Thanks for your help.
0
FLorenz
Asked:
FLorenz
  • 7
  • 5
1 Solution
 
northernchillCommented:
You need to put a message loop in place.  This should be an infinite loop (it will be broken).  In this loop you process window Messages and pass along only those that apply to your window.  When the appropriate message is received break the loop, close the window and your done.

MSG msg;
ShowWindow(m_hWnd,SW_SHOW);
for(;;)
{
    if(PeekMessage(&msg, NULL, 0, 0,PM_NOREMOVE)){
        if(msg.hwnd == m_hWnd)
        {
             GetMessage(&msg, NULL, 0, 0);
             if(msg.message == UWM_EXIT)
                break;
             TranslateMessage(&msg);
             DispatchMessage(&msg);
        }
    }
}
ShowWindow(m_hWnd,SW_HIDE);
DestroyWindow(m_hWnd);

Replacing UWM_EXIT with an appropriate value for your program.  
0
 
northernchillCommented:
Excuse me, the GetMessage call has to come before the if(msg.hwnd == m_hWnd)
0
 
FLorenzAuthor Commented:
Thanks northernchill,

While waiting for an answer from experts-exchange, I kept looking for some idea. I came to a very similar solution, inspired from
http://www.winprog.org/tutorial/message_loop.html
and from the Microsoft documentation.

Now, my custom window is created once, then shown and hidden several times: shown when needed by the main program, hidden when the user hits the "ok" or "cancel" button.

So I associated the following code with the "show" instruction

ShowWindow(m_hWnd,SW_SHOW);
while ( GetMessage ( &msg, NULL, 0, 0 ) > 0 )
  {
  TranslateMessage (&msg);
  DispatchMessage (&msg);
  }

and the following code with the "hide" instruction

ShowWindow(m_hWnd,SW_HIDE);
PostQuitMessage(0);

Looks simple isn't it? This seems to work perfectly.
Note the "while(GetMessage > 0)" instead of "while(GetMessage)", as suggested in the above-given reference. I also found a similar advice in Microsoft documentation.

Now, do you have any comment about this simplified version?
More specifically, what may I be missing by not calling the "PeekMessage" function nor doing the "if(msg.hwnd == m_hWnd)" test?

Thanks for your help. I'll give you the points anyway.
0
[Webinar On Demand] Database Backup and Recovery

Does your company store data on premises, off site, in the cloud, or a combination of these? If you answered “yes”, you need a data backup recovery plan that fits each and every platform. Watch now as as Percona teaches us how to build agile data backup recovery plan.

 
northernchillCommented:
The only thing that may occur is without the msg.hwnd == m_hWnd is that you will pass messages along to other windows in the application.  This may eliminate the Modality of your dialog.  The code I supplied was basic, you should also check the hwnd against child window handles if they are going to receive messages.

PeekMessage just looks at the message buffer to see if a message is waiting.  So you won't really loose too much by not calling this function. If you wanted to have some other processing while messages are not being relayed, then including the PeekMessage allows the program to continue through the loop if no message is available.  
0
 
FLorenzAuthor Commented:
Thanks for your comments and help.

To cope with child window handles, I tried replacing
  if ( msg.hwnd == m_hWnd )
by
  if ( IsCurWinOrSon ( msg.hwnd, m_hWnd ) )
with IsCurWinOrSon defined as

bool IsCurWinOrSon (
  HWND  hWndToCheck,
  HWND  hWndParent
  )

// Checks if hWndToCheck is hWndParent or (recursively) one of its sons.

  {
  if (hWndToCheck == (HWND)NULL)
    return false;
  else if (hWndToCheck == hWndParent)
    return true;
  else
    return IsCurWinOrSon ( GetParent(hWndToCheck), hWndParent);
  }

My window simply contains a combo-box plus "ok" and "cancel" buttons. Now, moving the window works, as well as hitting the buttons, but it freezes as soon as I touch the combo-box and I have to use the task manager to stop the application!!!

What is wrong?
0
 
northernchillCommented:
Where does it freeze?  Can you break into the program when it freezes using the debugger?  There is probably a loop somewhere that is not receiving an exit signal.
0
 
northernchillCommented:
Above TranslateMessage(&msg); add

if(PreTranslateMessage(&msg) == 0)
    TranslateMessage(&msg);

This allows proper handling for messages routed to child windows.

Sorry, it should have been in my original post.
0
 
FLorenzAuthor Commented:
Not yet solved, I am afraid.

Compiler says
error C2065: 'PreTranslateMessage' : undeclared identifier

From what I can find in the Microsoft documentation, this is an MFC function. Now, I am using SDK, not MFC...


I am still trying to find where it freezes exactly, but not yet found (I must say that I am not the original writer of this part of the program - always a big challenge).

What I can see is the following:
- when I click the combo box, it pulls down its options before the window completely freezes.
- just commenting out the "msg.hwnd" check is sufficient to make everything work fine (except that the window is not modal).

Seems to indicate that there is a message that is not dispatched while it should be. This is what I am trying to track. Now, it is so difficult to debug the message loop: messages keep flowing in all the time...
0
 
northernchillCommented:
Yes you are right.  My apologies, I am shifting out of MFC mode with this and sometimes still fall back into old train of thought.

Try dropping down the list, typing in the edit box of the combo box and then press enter. This should close the drop down and free the mouse to interact with the dialog again. I think what happens, is your drop list sets itself up to track the mouse, but the mouse messages don't make it there.  However, pressing Enter in the edit should tell the combo box to release its mouse tracking all should be well.

What does this mean for your program?  I am not sure.  I will have to think about it a little.  The answer is not coming into my mind right now.

0
 
northernchillCommented:
Allright, here the answer that should have been posted to begin with:

Use a dialog template (still acceptable in your SDK project?).

Call
::DialogBox(m_hInstance,MAKEINTRESOURCE(YOUR_TEMPLATE_ID),NULL,YourDialogProc);

This will create a Modal Dialog Box for you (and handle the message dispatching).

YourDialogProc looks something like:
BOOL CALLBACK StdDialogProc(HWND hwndDlg, UINT uMsg, PARAM wParam, LPARAM lParam)
{
   switch(uMsg)
   {
// Handle messages here
   case WM_INITDIALOG:
      // Initialize your dialog box
   case WM_WHATEVER
      // Perform whatever processing you need done depending on the message
   case WM_COMMAND:
      switch (LOWORD(wParam)) {
      case IDOK:
         EndDialog(hwndDlg,0);
      case IDCANCEL:
         EndDialog(hwndDlg,
      }
    default:
      return FALSE; // Message wasn't handled
   }
return TRUE; // Message was handled

}

If you can use a template then this is the way to go.
0
 
FLorenzAuthor Commented:
Regarding keyboard input:
Indeed, the keyboard works: I can move in the list for instance with the keyboard "up" and "down" arrows, then select an item by pressing the "enter" key. It closes the drop down and the mouse can be used again, as you predicted.
Well done! Looks like you have some experience...
I think I'll have to dig out the portion of code managing that combo box and try to understand how it works exactly.
Not easy because the whole thing has been based on (a modified version of) MIRO to make it compatible to both Windows and OSF/Motif. So there are layers over layers over layers...

Regarding the dialog template:
Why not a dialog template? This has been my first question too. Not so easy to change due to the layered architecture I mentionned. I asked the guy who originally did this part of the program but he does not remember exactly.
Note that this window was originaly not modal and it was ok. The problem is that it needs to be modal now, so I try just to change this aspect and nothing else. Should be the fastest way, although I begin to doubt.

Thanks for your appreciated help.
0
 
FLorenzAuthor Commented:
At last it works. Yeah!

The problem is with the combo-box. When one clicks on the selection field, a message is sent to the combo-box that drops down the list-box. Then the list-box captures the mouse, waiting for a selection. The problem is that the list-box handle is apparently not a (grand)child of the combo-box (a bug of Windows?) so that my message loop will not execute further mouse messages.

Now, finding a solution gave me some headhache. As the combo-box is a predefined Windows control, there is no means to change the characteristics of its embedded list-box.

I eventually found a quite tricky means. I simply changed my message filter in

  if ( IsCurWinOrSon ( msg.hwnd, hwnd ) || msg.hwnd == GetCapture() )

The messages are still executed if they adress the handle that captured the mouse i.e. the list-box. This works.

Does anyone see any sort of problem with this method? (or is there a less tricky method?)
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 7
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now