Brian K
Brian K

Reputation: 721

SetTimer Function on CWinApp failing to be set

I am currently making a C++ MFC application, and I need a timer to call a function called OnTimer every 30 seconds or so. Right now, I have a class that looks like this:

class CMyApp : public CWinApp
{
    // Do some stuff...
    DECLARE_MESSAGE_MAP()

    BOOL InitInstance()
    {
        // m_pMainWnd is an object in CWinApp that allows me to render a window

        m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);

        // Do some stuff with this window

        ::SetTimer(NULL, 0, 30000, 0);
    }

    afx_msg void OnTimer(WPARAM wParam, LPARAM lParam)
    {
        // I want this function to execute every 30 seconds
        // This function manipulates the window
    }
}

BEGIN_MESSAGE_MAP(CMyApp, CWinApp)
    ON_THREAD_MESSAGE(WM_TIMER, OnTimer)
END_MESSAGE_MAP()

CMyApp theApp;

This method does get OnTimer to be called, but not every 30 seconds. In fact, OnTimer seems to get called hundreds of times a minute now. My question is: how do I set my class's timer?

Things I've tried

I've tried changing the user extension from ON_THREAD_MESSAGE to ON_WM_TIMER, to ON_COMMAND, and to ON_MESSAGE. For anything that isn't ON_THREAD_MESSAGE, I get the error

error C2440: 'static_cast' : cannot convert from 'void (__thiscall CMyApp::* )(WPARAM,LPARAM)' to 'LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)'

I'm not sure, but I think SetTimer may be manipulating some CWinApp specific function, while the CWnd SetTimer is not being manipulated, and is set to some default value. I'm pretty much in the dark here though, any help would be appreciated.

Upvotes: 0

Views: 1117

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31619

ON_THREAD_MESSAGE is for user defined messages, not WM_TIMER

According to SetTimer documentation, the window handle must be valid, and the timer identifier must be non-zero to create a new timer. Example:

::SetTimer(m_pMainWnd->m_hWnd, 1, 30000, NULL);
or 
m_pMainWnd->SetTimer(1, 30000, NULL);

The message can be handled in main GUI Window. For example CMainFrame, CMyCMDIFrameWnd, or whatever m_pMainWnd points to.

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
    ON_WM_TIMER()
    ...
END_MESSAGE_MAP()

void CMainFrame::OnTimer(UINT id)
{
    TRACE("OnTimer(%d)\n", id);
}

Alternatively you can use NULL for window handle in ::SetTimer but you must supply a call back function:

VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    TRACE("TimerProc\n");
}

::SetTimer(NULL, 2, 30000, TimerProc);

Upvotes: 3

Related Questions