VK_TAB Message NOT getting to correct dialog

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
rickatseasoftAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AndyAinscowFreelance programmer / ConsultantCommented:
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
rickatseasoftAuthor Commented:
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
rickatseasoftAuthor Commented:
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
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

DanRollinsCommented:
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
rickatseasoftAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
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
rickatseasoftAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
I'll have a look and get back - tomorrow is soonest though.
0
DanRollinsCommented:
>> 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
AndyAinscowFreelance programmer / ConsultantCommented:
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
rickatseasoftAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.