1
votes

I created an MFC dialog based app in VS2010, and wanted to add timer to update a picture controller every 3 seconds. But the OnTimer method never worked.

I have used Class Wizard to add WM_TIMER into the message queue, which turned out to be as following:

BEGIN_MESSAGE_MAP(CxxxxDlg, CDialogEx)
    ON_WM_PAINT()
    ON_BN_CLICKED(IDOK, &CxxxxDlg::OnBnClickedOK)
    ON_WM_TIMER()
END_MESSAGE_MAP()

In xxxxDlg.cpp, I put SetTimer method in OnInitDialog:

BOOL CxxxxDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    SetIcon(m_hIcon, TRUE);
    SetIcon(m_hIcon, TRUE);

    _imageCounter = 1;
    _isMale = 3;
    _testNum = 0;

    SetTimer(123, 2000, NULL);

    bFullScreen = false;
    OnFullShow();
    updateImages();
    UpdateData();

    return TRUE;
}

The OnTimer method was declard in xxxxdlv.h:

public:
    afx_msg void OnTimer(UINT_PTR nIDEvent);

When I run the app, the SetTimer returned 123. So everything should be all right here. But the program never reached the breakpoint I set in the 1st line of OnTimer method!

Then I wrote another hello world project only to test the timer. I set the timer in exactly the same way and it worked well.

So I thought the OnFullShow() method may be the problem. This method was used to change the window into full screen mode. I comment this line , but still OnTimer never worked.

I have check the questions here. But it doesn't help.

Does anyone know where the problem comes from? Thanks!

PS. I did receive some warnings of memory leaks. Did this matter?

1
Could it be that your WM_PAINT function is somehow looping so that the WM_TIMER message doesn't get processed? [try commenting out your paint function to check]Edward Clements
WM_TIMER is a low priority message (like WM_PAINT and WM_MOUSEMOVE). It is only generated if the message queue is empty and there are no invalid window regions and there is no mouse input. If you fail to meet either of those conditions you will never get a WM_TIMER, even though the timer was properly installed.IInspectable
@EdwardClements I comment this line out but the problem is still there. Thanks.thundertrick
@IInspectable Thanks. You remind me of the PreTranslateMessage method I used for the keyboard. If the level of message did matter, this method might not receive WM_TIMER, isn't it? I'll update my question later. Thanks again :)thundertrick
Funny, you've mentioned PreTranslateMessage. What do you return by default from that method? You should return FALSE for all other messages you are not translating.cha

1 Answers

1
votes

Thanks to @IInspectable. I found a tech support here. It fully explains the cause and tells one solution:

// Rewrite PreTranslateMessage method
BOOL CMyApp::PreTranslateMessage( MSG *pMsg )
{
   // If this is a timer callback message let it pass on through to the
   // DispatchMessage call.
   if( (pMsg->message==WM_TIMER) && (pMsg->hwnd==NULL) )
       return FALSE;
   ...
   // The rest of your PreTranslateMessage goes here.
   ...

   return CWinApp::PreTranslateMessage(pMsg);
}

This solution does not solve my problem but gives me a hint. PreTranslateMessage method should be rewritten to let WM_TIMER pass on through to the DispatchMessage call. But if you are using PreTranslateMessage to deal with other messages, WM_KEYDOWN for example, the solution above may not work. It seems to be a problem about priority. In the end, I solve it using switch instead of if:

// Rewrite PreTranslateMessage method
BOOL CMyApp::PreTranslateMessage( MSG *pMsg )
{
   // If this is a timer callback message let it pass on through to the
   // DispatchMessage call.
   switch(pMsg->message)
   {
    case WM_KEYDOWN: // your codes
    case WM_TIMER: return false;
    ...
   }
   ...
   // The rest of your PreTranslateMessage goes here.
   ...

   return CWinApp::PreTranslateMessage(pMsg);
}

I hope this will help anyone who has similar problem.

PS. pMsg->hwnd==NULL is removed in switch and I am not sure if it is safe.