?
Solved

VK_TAB Message NOT getting to correct dialog

Posted on 2006-03-29
12
Medium Priority
?
718 Views
Last Modified: 2013-11-20
I have a MDI project that, based on the user selected item, performs an OnNewDocument() which opens a view.  The view then examines a variable in the CDocument class to determine which CDialog to open in a non-modal state.  The CDialog opens as a child of the CView, and may or may not have a tab control, so there might be many additional CDialogs that are children.

Some of the CDialogs are simple input and output functions with no need to create a thread.  Some,. however, do call AfxBeginThread to a non-interactive thread---a function as opposed to a class is called.

Based on some excellent advice from this forum, I added a message handler after the AfxBeginThread() so that the CView of the calling CDialog could respond to minimize messages, and other CDialogs could continue to process keystrokes while the thread processed.

My problem is that the VK_TAB key is not reaching the CDialog with focus.  Whenever I hit the TAB key, the system beeps, and nothing else happens.  In the message handler in the CDialog that called the thread, I can see the VK_KEYDOWN message with a param of VK_TAB.  Unfortunately, that message never reaches the dialog with focus.

I have read other posts on this forum that suggest that the focus is flying off of the Dialog.  If it is it must be going to the CView that is the parent of the dialog, because the CView fails to dim as I would expect with a loss of focus.  As the dialogs have no header, I am unable to tell if they have the focus or not.

BTW: the bahavior is identical whether it is Document->View->Dialog or Document->View->Dialog->TabControl->MoreDialogs

Any help will be greatly appreciated.

Rick
0
Comment
Question by:rickatseasoft
  • 5
  • 5
  • 2
12 Comments
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16326379
You could try

BOOL CEndlessItemDlg::PreTranslateMessage(MSG* pMsg)
{
    if(pMsg->message == WM_KEYDOWN)
      {
            switch(pMsg->wParam)
            {
            case VK_TAB:
                        //Pass it the direction of the tab
                        if(GetKeyState(VK_SHIFT) & 0x80)      //true if shift key pressed
                              PrevDlgCtrl();
                        else
                              NextDlgCtrl();
                  }
                  return true;
0
 

Author Comment

by:rickatseasoft
ID: 16326615
Andy:

I already tried that.  See the fragment below:

This is in the Dialog that did NOT launch the thread.

BOOL CAmarun31eDlg::PreTranslateMessage(MSG* pMsg)
{
      // TODO: Add your specialized code here and/or call the base class
            
      if(pMsg->message==WM_KEYDOWN&&pMsg->wParam==13){ //enter or return key pressed
            pMsg->wParam=VK_TAB;      
      }
      if(pMsg->message==WM_KEYDOWN&&pMsg->wParam==VK_TAB){ //enter or return key pressed
            AfxMessageBox("Hello");
      }
      
      return CDialog::PreTranslateMessage(pMsg);
}

If I press the TAB key I get a "Hello" message up to the point where the thread launches.  As soon as the thread launches, all I get is a BEEP whenever I press the TAB key.

Incidentally, I just included the PreTranslateMessage() in the CView class to see if, somehow, the message was intercepted there.  It wasn't.

Also, the !IsDialogMessage() requires only one argument unless prefaced with the ::.  I tried both instances.  Should I be thinking in terms of getting the FocusWindow, and sending the posting or sending the message directly?  I've never tried that, but I wonder.

I hope you are not running out of ideas, because I certainly have.

Rick
0
 

Author Comment

by:rickatseasoft
ID: 16326671
I still don't understand why the Dialog that no longer has focus is receiving the messages at all.  

Should I try go trap the VK_TAB messages at the application level, and route them to the focus window?

Rick
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 49

Expert Comment

by:DanRollins
ID: 16327975
Standard advice on diagnosing these issues:  
Bring up Spy++ and watch the messages fly around.  It soon becomes obvious which window is receiving which message (at least it's a lot easier that adding PreTranslateMessage fns and MessageBox all over the place :-)
0
 

Author Comment

by:rickatseasoft
ID: 16328218
I have never tried Spy++, but gave it a go anyway.  It would seem that most messages are actually arriving at the CDialog.  It might also be the case that the Killfocus message is arriving also although it is hard to tell, and I am not absolutely certain what I am looking at.

I'm wondering about killing the CDialog that launches the thread to see what happens.  I would like to avoid this because I depend on the CDocument class to do the final cleanup after the thread finishes.

Any more ideas will be greatly appreciated.

MAHESH, if you are there.  I really didn't understand your posts on the other thread, it seemed like you might have misunderstood the question (of course it is possible that I misunderstood the answer), and the functions that you suggested didn't seem to be documented by MS.  If you think that they are germane here, please give me a little more help with what you were trying to say.

Thanks, Rick
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16329730
I don't understand what is happening with your TAB key presses.

However this is what I do with a thread.
I start a thread and as part of the information I give it the handle of the window that 'owns' it (NOT the CWnd* pointer, the window handle from GetSafeHwnd()).
Now I let the rest of the function run where I launch the thread.  (No WaitForSingleObject or similar).
When the thread finishes then it uses ::PostMessage(hWndOwner, MY_CUSTOM_MESSAGE, wParam, lParam) to inform the window that the thread has finished.  wParam and lParam can pass information to identify the thread.

Why - no need to do any fancy message loop (as you require), let windows do it for me.  
After all threads are to allow two 'things' to run independantly of one another.  As soon as one starts a thread and locks the main thread to wait for the thread to finish it a) requires a kludge to act is if the main thread is not locked and b) makes me wonder why a thread is used at all, why not just have the code in the main thread and a message pump.  (OK, there *could* be instances when you really do require another thread).
0
 

Author Comment

by:rickatseasoft
ID: 16333307
Andy:

Thanks for providing the answer.  It works really well.  I do have a couple of follow-on questions and comments if you don't mind.

The reason for the thread is so that a long report 1 minute to perhaps several hours can be run without denying access to the rest of the program.  I know that they could just run another instance of the program, but, if we are going to require that, they why go to the trouble of using an MDI?  Also, the reason for keeping the dialog on the screen is for the user to see what is happening, what reports are running, etc.


My implementation is a little different from what you suggested, and I wonder if there are any "gotchas" in here that I haven't thought of.

I added a AllDone member to the structure that is being sent to the thread.  Until the thread completes, AllDone is false.  When the  thread finishes, it sets AllDone to 1, and sends a WM_CLOSE message to the CDialog that started the thread.  So the flow is something like:

xxxxxx:OnOK()
{
     Setup the Thread Arguments
     Load the wait cursor and set m_bCursor to TRUE
    Disable the OK and Cancel Buttons, and Set a flag in the parent to ignore WM_CLOSE (The parent is a MDI CView class, and we don't want the user pressing X to close the thread while it is processing.)
   if(pThread=AfxBeginThread(...)return;
   else{
         AfxMessageBox("...");
         Turn WM_CLOSE back on in the parent
         CDialog::OnOK();
   }

void xxxxxxxx::OnClose()
{
      // TODO: Add your message handler code here and/or call default
      if(m_bCursor&&!m_ta.AllDone)return;      
      if(m_ta.AllDone==1){  //The Thread has finished, and we need to check for printing, etc.
            m_ta.AllDone=2;  //Prevent Re-entry to this section
            turn WM_CLOSE back on in the parent
                      revert to normal cursor
            report any errors found by the thread
            if there is a file to print, do so -- We use a specialized report viewer
            Call OnCloseDocument in the CDocument Class;
            return;
      }
                Any subsequent WM_CLOSE messages will be processed here.
      CDialog::OnClose();
}

I used the WM_CLOSE message simply because it was easy.

One last thing, the computer beeps on return from the thread.  Do you know why?

Do you see any problems with the way I have implemented this?

Thanks again for all of your help; it has been invaluable.

Rick
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16333341
I'll have a look and get back - tomorrow is soonest though.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 16335290
>> As the dialogs have no header, I am unable to tell if they have the focus or not.

Your comment might indicate that you are using the borderless "modal-looking" style settings for your modeless dialogs.  Just for fun, try changing the style to give it a standard pop-up appearance with a caption (like the other common modeless dialgs such as Word's "Find/Find Again" box.)  If nothing else, this will help you see when the floater gets and loses focus.

>> My problem is that the VK_TAB key is not reaching the CDialog with focus.

Modeless dialogs are peculiar.. .they are is a state that both "has the focus" and "does not have the focus" -- the whole point is that the user can continue doing things in the main window while the satellite window floats around above it... also ready to accept input.  

Another thought:
The TAB key has special meaning -- the IsDialgMessage() fn processes it (and "eats it" if it has no meaning) early on in the event cycle.

=-=-=-=-=-=
I'm not sure I follow your threading logic, but I can guarantee that if you have a worker thread interacting with the U/I, you will encounter strange problems.   The best way to indicate progress, update status text etc. in a multi-threaded environment is to just have the worker thread access (write to) program variables.  Then in the U/I  thread, have a Timer handler that looks at those variables and updates the screen to match them.

-- Dan
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16339962
From what I see with the OnOK, OnClose it looks as if it should work.

One point that maybe you ought to consider.  What if the user wants to abort the process?

I would not send the WM_CLOSE but another message that starts the closing procedure.  Receipt of that message would set a flag in the dialog that it is a thread based closure and generate the WM_CLOSE to send to itself.  In the OnClose I would check this value.  If it is thread based close then the thread has finished so it closes and tidies up  normally.  However if the flag is not set then the user is trying to close.  (Prompt for confirmation?)  At this point you notify the thread (raise an event) that it is to stop.  Now exit the close and wait for the thread to signal it has finished by the custom message which starts the close routine again.
0
 

Author Comment

by:rickatseasoft
ID: 16341487
Andy:

Thanks for the pointers.  Since the WorkerThread has no message pump, I guess that the notification would need to be some flag set, and the thread would simply have to check the flag periodically, or is there a better way?

Rick
0
 
LVL 45

Accepted Solution

by:
AndyAinscow earned 2000 total points
ID: 16341627
You can raise an event and check for the event.

eg code like this in thread

      while (WaitForSingleObject(pInfo->hEvent, 0) != WAIT_OBJECT_0)
      {
            DoSomethingInLoop();

            if (dwBytesRead > 0)
            {
                  if (bSomeSetting)
                        SendMessage(pInfo->hWnd, pInfo->msgSomethingTrue, 0, 0);
                  else
                        SendMessage(pInfo->hWnd, pInfo->msgSomethingFalse, 0, (LPARAM) 1234);
            }
      }


and in the dialog, raise the event
m_eventClose.SetEvent()
where pInfo->hEvent has the handle of m_eventClose.
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
This Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Suggested Courses

840 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