Discussion:
Thread and Timer
(too old to reply)
Stefano
2008-03-12 07:55:52 UTC
Permalink
Hi,
I'd like to use a timer inside a thread (CWinThread derived class).
If I use the CALLBACK procedure I get the timer, if I don't use the
CALLBACK and handle in OnTimer I never get the message.
Here is the code:

// WatchDog.cpp : implementation file
//

#include "stdafx.h"
#include "xcrashreport.h"
#include "WatchDog.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CString g_sFileName;
HWND g_hWnd;
UINT g_iMessage;
UINT g_iTimer;
UINT g_iTimerTimeout;

/////////////////////////////////////////////////////////////////////////////
// CWatchDog

void CALLBACK OnTimerProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
)
{ // OnTimerProc

if (idEvent == g_iTimer)
{
PostMessage(g_hWnd, g_iMessage, WPARAM (0), LPARAM (0));


KillTimer( NULL, g_iTimer );
AfxEndThread( 0, false );

}

} // OnTimerProc

IMPLEMENT_DYNCREATE(CWatchDog, CWinThread)

BOOL CWatchDog::InitInstance()
{
// TODO: perform and per-thread initialization here
return TRUE;
}

int CWatchDog::ExitInstance()
{
// TODO: perform any per-thread cleanup here
return CWinThread::ExitInstance();
}

BOOL CWatchDog::IsIdleMessage( MSG* pMsg )
{
if ( !CWinThread::IsIdleMessage( pMsg ) || ( pMsg->message ==
WM_TIMER ) )
{
return FALSE;
}
return TRUE;
}



BEGIN_MESSAGE_MAP(CWatchDog, CWinThread)
//{{AFX_MSG_MAP(CWatchDog)
// NOTE - the ClassWizard will add and remove mapping macros here.

//}}AFX_MSG_MAP

ON_THREAD_MESSAGE(WM_TIMER, OnTimer)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWatchDog message handlers

void CWatchDog::Monitor(CString sFileName, HWND hWnd, UINT iMessage,
int iFrequency, int iTimeout)
{
//g_iTimer = ::SetTimer(NULL, NULL, iFrequency, (TIMERPROC)
OnTimerProc);

g_iTimer = SetTimer(NULL, 0, iFrequency, NULL);
g_hWnd = hWnd;
g_iMessage = iMessage;
}


void CWatchDog::OnTimer(WPARAM wParam, LPARAM lParam)

{
if ((UINT) wParam == g_iTimer)
{
PostMessage(g_hWnd, g_iMessage, WPARAM (0), LPARAM (0));
}
}

Thanks in advance,
Stefano
AliR (VC++ MVP)
2008-03-12 14:26:40 UTC
Permalink
You dont need to do all of that.
Put the entry to WM_TIMER in your threads message handler,
ON_THREAD_MESSAGE(WM_TIMER,OnTimer)

The OnTimer should have a signature like this:
afx_msg void OnTimer(WPARAM nTimerID,LPARAM);

then call
m_TimerID = SetTimer(NULL,0,time,NULL);

and wait for OnTimer to get called.

AliR.
Post by Stefano
Hi,
I'd like to use a timer inside a thread (CWinThread derived class).
If I use the CALLBACK procedure I get the timer, if I don't use the
CALLBACK and handle in OnTimer I never get the message.
// WatchDog.cpp : implementation file
//
#include "stdafx.h"
#include "xcrashreport.h"
#include "WatchDog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CString g_sFileName;
HWND g_hWnd;
UINT g_iMessage;
UINT g_iTimer;
UINT g_iTimerTimeout;
/////////////////////////////////////////////////////////////////////////////
// CWatchDog
void CALLBACK OnTimerProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
)
{ // OnTimerProc
if (idEvent == g_iTimer)
{
PostMessage(g_hWnd, g_iMessage, WPARAM (0), LPARAM (0));
KillTimer( NULL, g_iTimer );
AfxEndThread( 0, false );
}
} // OnTimerProc
IMPLEMENT_DYNCREATE(CWatchDog, CWinThread)
BOOL CWatchDog::InitInstance()
{
// TODO: perform and per-thread initialization here
return TRUE;
}
int CWatchDog::ExitInstance()
{
// TODO: perform any per-thread cleanup here
return CWinThread::ExitInstance();
}
BOOL CWatchDog::IsIdleMessage( MSG* pMsg )
{
if ( !CWinThread::IsIdleMessage( pMsg ) || ( pMsg->message ==
WM_TIMER ) )
{
return FALSE;
}
return TRUE;
}
BEGIN_MESSAGE_MAP(CWatchDog, CWinThread)
//{{AFX_MSG_MAP(CWatchDog)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
ON_THREAD_MESSAGE(WM_TIMER, OnTimer)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWatchDog message handlers
void CWatchDog::Monitor(CString sFileName, HWND hWnd, UINT iMessage,
int iFrequency, int iTimeout)
{
//g_iTimer = ::SetTimer(NULL, NULL, iFrequency, (TIMERPROC)
OnTimerProc);
g_iTimer = SetTimer(NULL, 0, iFrequency, NULL);
g_hWnd = hWnd;
g_iMessage = iMessage;
}
void CWatchDog::OnTimer(WPARAM wParam, LPARAM lParam)
{
if ((UINT) wParam == g_iTimer)
{
PostMessage(g_hWnd, g_iMessage, WPARAM (0), LPARAM (0));
}
}
Thanks in advance,
Stefano
Joseph M. Newcomer
2008-03-12 15:39:20 UTC
Permalink
That's right. That's what you would expect in a thread which has no message pump, or for
which you blocked the message pump.

First, callback timers are about the most useless entities in all of Windows. Try to
avoid them because they give no value, and only add gratuitous complexity to the problem,
such as the pointless global variables you have. These would not be needed had the timer
callback been designed correctly, which it wasn't.

The presence of global variables should already be a sign that the approach is wrong.
There are very limited number of APIs for which these make sense at all, and every one of
those APIs represents a faulty design on the part of Microsoft.
Post by Stefano
Hi,
I'd like to use a timer inside a thread (CWinThread derived class).
If I use the CALLBACK procedure I get the timer, if I don't use the
CALLBACK and handle in OnTimer I never get the message.
// WatchDog.cpp : implementation file
//
#include "stdafx.h"
#include "xcrashreport.h"
#include "WatchDog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CString g_sFileName;
HWND g_hWnd;
UINT g_iMessage;
UINT g_iTimer;
UINT g_iTimerTimeout;
/////////////////////////////////////////////////////////////////////////////
// CWatchDog
void CALLBACK OnTimerProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
)
{ // OnTimerProc
if (idEvent == g_iTimer)
{
PostMessage(g_hWnd, g_iMessage, WPARAM (0), LPARAM (0));
KillTimer( NULL, g_iTimer );
AfxEndThread( 0, false );
}
} // OnTimerProc
IMPLEMENT_DYNCREATE(CWatchDog, CWinThread)
BOOL CWatchDog::InitInstance()
{
// TODO: perform and per-thread initialization here
return TRUE;
}
int CWatchDog::ExitInstance()
{
// TODO: perform any per-thread cleanup here
return CWinThread::ExitInstance();
}
BOOL CWatchDog::IsIdleMessage( MSG* pMsg )
{
if ( !CWinThread::IsIdleMessage( pMsg ) || ( pMsg->message ==
WM_TIMER ) )
****
This code should not exist. Drop this whole function!
****
Post by Stefano
{
return FALSE;
}
return TRUE;
}
BEGIN_MESSAGE_MAP(CWatchDog, CWinThread)
//{{AFX_MSG_MAP(CWatchDog)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
ON_THREAD_MESSAGE(WM_TIMER, OnTimer)
****
Why not do something very simple: ON_WM_TIMER()?

You have added tons of complexity to what is a trivial problem.
****
Post by Stefano
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWatchDog message handlers
void CWatchDog::Monitor(CString sFileName, HWND hWnd, UINT iMessage,
int iFrequency, int iTimeout)
{
//g_iTimer = ::SetTimer(NULL, NULL, iFrequency, (TIMERPROC)
OnTimerProc);
g_iTimer = SetTimer(NULL, 0, iFrequency, NULL);
g_hWnd = hWnd;
g_iMessage = iMessage;
****
Why not
SetTimer(iFrequency, NULL);
which is so much simpler?
****
Post by Stefano
}
void CWatchDog::OnTimer(WPARAM wParam, LPARAM lParam)
{
if ((UINT) wParam == g_iTimer)
{
PostMessage(g_hWnd, g_iMessage, WPARAM (0), LPARAM (0));
}
}
****
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER. Get rid of them. I can't even imagine why they are needed at all, since they
coudl be member variables of the CWatchDog class.

Treat tasks that require global variables as deeply suspect.
joe
****
Post by Stefano
Thanks in advance,
Stefano
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
AliR (VC++ MVP)
2008-03-12 15:50:39 UTC
Permalink
See below.
Post by Joseph M. Newcomer
Post by Stefano
BEGIN_MESSAGE_MAP(CWatchDog, CWinThread)
//{{AFX_MSG_MAP(CWatchDog)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
ON_THREAD_MESSAGE(WM_TIMER, OnTimer)
****
Why not do something very simple: ON_WM_TIMER()?
You have added tons of complexity to what is a trivial problem.
****
Post by Stefano
END_MESSAGE_MAP()
Becuase ON_WM_TIMER will try to map to a function void (__thiscall
CWnd::*)(UINT_PTR)
That is definitely not going to work on a CWinThread
Post by Joseph M. Newcomer
Post by Stefano
/////////////////////////////////////////////////////////////////////////////
// CWatchDog message handlers
void CWatchDog::Monitor(CString sFileName, HWND hWnd, UINT iMessage,
int iFrequency, int iTimeout)
{
//g_iTimer = ::SetTimer(NULL, NULL, iFrequency, (TIMERPROC)
OnTimerProc);
g_iTimer = SetTimer(NULL, 0, iFrequency, NULL);
g_hWnd = hWnd;
g_iMessage = iMessage;
****
Why not
SetTimer(iFrequency, NULL);
which is so much simpler?
****
Because CWinThread does not have a SetTimer method, he has to use the API.


AliR.
Stefano
2008-03-13 09:42:20 UTC
Permalink
Post by Joseph M. Newcomer
Why not do something very simple: ON_WM_TIMER()?
I tryed with ON_WM_TIMER but it's never called.
Post by Joseph M. Newcomer
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER.  Get rid of them.  I can't even imagine why they are needed at all, since they
coudl be member variables of the CWatchDog class.
Yes you are right. I copyed and paste the code from the the callback.
Joseph M. Newcomer
2008-03-13 14:03:21 UTC
Permalink
Is it a UI thread? Is the timer associated with a window in the UI thread? (You may have
to create an invisible top-level window to get this to work...)
joe
Post by Stefano
Post by Joseph M. Newcomer
Why not do something very simple: ON_WM_TIMER()?
I tryed with ON_WM_TIMER but it's never called.
Post by Joseph M. Newcomer
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER.  Get rid of them.  I can't even imagine why they are needed at all, since they
coudl be member variables of the CWatchDog class.
Yes you are right. I copyed and paste the code from the the callback.
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
AliR (VC++ MVP)
2008-03-13 14:18:09 UTC
Permalink
Joe you must be kidding, he needs a window in his UI thread just use timers?

::SetTimer works just fine in CWinThread. All you have to do is set a
ON_THREAD_MESSAGE(WM_TIMER,OnTimer) in your message map.

AliR.
Post by Joseph M. Newcomer
Is it a UI thread? Is the timer associated with a window in the UI thread? (You may have
to create an invisible top-level window to get this to work...)
joe
Post by Stefano
Post by Joseph M. Newcomer
Why not do something very simple: ON_WM_TIMER()?
I tryed with ON_WM_TIMER but it's never called.
Post by Joseph M. Newcomer
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER. Get rid of them. I can't even imagine why they are needed at
all, since they
coudl be member variables of the CWatchDog class.
Yes you are right. I copyed and paste the code from the the callback.
Joseph M. Newcomer [MVP]
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer
2008-03-13 18:39:37 UTC
Permalink
To get WM_TIMER messages, yes. This trick is actually pretty common; for example,
CAsyncSocket creates a dummy window to handle callback notifications, which are sent as
window messages; SAPI creates a dummy window to handle Text-to-Speech notifications; and I
knew about the trick years ago. Any generic derived-direct-from-CWnd class will do
nicely.

Timer callbacks will work without a window, but overall they suck badly, because there is
no user-specified data object passed in (as I explain to my students, the callback
function has four parameters, three of which are totally useless and one of which is never
used anyway, so why do we have four useless parameters? Bad design. You can't really
make a thread-safe callback without a lot of effort; __declspec(thread) helps, or direct
use of Thread Local Storage.

My "timer-in-a-CDocument" essay shows how this can be done in a way that lets a CDocument
maintain an internal timer which has nothing to do with a view, which is useful if the
"document" is virtual and is implemented at the other end of a communication wire (RS232,
RS485, USB, etc.) and the timeouts deal with the document's communication with its virtual
implementation and should not be visible to the views.
joe
Post by AliR (VC++ MVP)
Joe you must be kidding, he needs a window in his UI thread just use timers?
::SetTimer works just fine in CWinThread. All you have to do is set a
ON_THREAD_MESSAGE(WM_TIMER,OnTimer) in your message map.
AliR.
Post by Joseph M. Newcomer
Is it a UI thread? Is the timer associated with a window in the UI
thread? (You may have
to create an invisible top-level window to get this to work...)
joe
Post by Stefano
Post by Joseph M. Newcomer
Why not do something very simple: ON_WM_TIMER()?
I tryed with ON_WM_TIMER but it's never called.
Post by Joseph M. Newcomer
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER. Get rid of them. I can't even imagine why they are needed at
all, since they
coudl be member variables of the CWatchDog class.
Yes you are right. I copyed and paste the code from the the callback.
Joseph M. Newcomer [MVP]
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
AliR (VC++ MVP)
2008-03-13 19:44:56 UTC
Permalink
I'm not talking about callbacks. I'm talking about using
ON_THREAD_MESSAGE(WM_TIMER,OnTimer)

I guess I will let the code explain it.

class CMyThread : public CWinThread
{
DECLARE_DYNCREATE(CMyThread)

protected:
CMyThread(); // protected constructor used by dynamic creation
virtual ~CMyThread();

public:
virtual BOOL InitInstance();
virtual int ExitInstance();

protected:
afx_msg void OnTimer(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()

private:
UINT m_TimerID;
};



IMPLEMENT_DYNCREATE(CMyThread, CWinThread)

CMyThread::CMyThread()
{
}

CMyThread::~CMyThread()
{
}

BOOL CMyThread::InitInstance()
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
return TRUE;
}

int CMyThread::ExitInstance()
{
// TODO: perform any per-thread cleanup here
return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CMyThread, CWinThread)
ON_THREAD_MESSAGE(WM_TIMER,OnTimer)
END_MESSAGE_MAP()

// CMyThread message handlers

void CMyThread::OnTimer(WPARAM nTimerID,LPARAM)
{
if (nTimerID == m_TimerID)
{
MessageBeep(1);
}
}


AliR.
Post by Joseph M. Newcomer
To get WM_TIMER messages, yes. This trick is actually pretty common; for example,
CAsyncSocket creates a dummy window to handle callback notifications, which are sent as
window messages; SAPI creates a dummy window to handle Text-to-Speech notifications; and I
knew about the trick years ago. Any generic derived-direct-from-CWnd class will do
nicely.
Timer callbacks will work without a window, but overall they suck badly, because there is
no user-specified data object passed in (as I explain to my students, the callback
function has four parameters, three of which are totally useless and one of which is never
used anyway, so why do we have four useless parameters? Bad design. You can't really
make a thread-safe callback without a lot of effort; __declspec(thread) helps, or direct
use of Thread Local Storage.
My "timer-in-a-CDocument" essay shows how this can be done in a way that lets a CDocument
maintain an internal timer which has nothing to do with a view, which is useful if the
"document" is virtual and is implemented at the other end of a
communication wire (RS232,
RS485, USB, etc.) and the timeouts deal with the document's communication with its virtual
implementation and should not be visible to the views.
joe
Post by AliR (VC++ MVP)
Joe you must be kidding, he needs a window in his UI thread just use timers?
::SetTimer works just fine in CWinThread. All you have to do is set a
ON_THREAD_MESSAGE(WM_TIMER,OnTimer) in your message map.
AliR.
Post by Joseph M. Newcomer
Is it a UI thread? Is the timer associated with a window in the UI
thread? (You may have
to create an invisible top-level window to get this to work...)
joe
Post by Stefano
Post by Joseph M. Newcomer
Why not do something very simple: ON_WM_TIMER()?
I tryed with ON_WM_TIMER but it's never called.
Post by Joseph M. Newcomer
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER. Get rid of them. I can't even imagine why they are needed at
all, since they
coudl be member variables of the CWatchDog class.
Yes you are right. I copyed and paste the code from the the callback.
Joseph M. Newcomer [MVP]
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer
2008-03-13 23:50:06 UTC
Permalink
Learn something new every day...
joe
Post by AliR (VC++ MVP)
I'm not talking about callbacks. I'm talking about using
ON_THREAD_MESSAGE(WM_TIMER,OnTimer)
I guess I will let the code explain it.
class CMyThread : public CWinThread
{
DECLARE_DYNCREATE(CMyThread)
CMyThread(); // protected constructor used by dynamic creation
virtual ~CMyThread();
virtual BOOL InitInstance();
virtual int ExitInstance();
afx_msg void OnTimer(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
UINT m_TimerID;
};
IMPLEMENT_DYNCREATE(CMyThread, CWinThread)
CMyThread::CMyThread()
{
}
CMyThread::~CMyThread()
{
}
BOOL CMyThread::InitInstance()
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
return TRUE;
}
int CMyThread::ExitInstance()
{
// TODO: perform any per-thread cleanup here
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(CMyThread, CWinThread)
ON_THREAD_MESSAGE(WM_TIMER,OnTimer)
END_MESSAGE_MAP()
// CMyThread message handlers
void CMyThread::OnTimer(WPARAM nTimerID,LPARAM)
{
if (nTimerID == m_TimerID)
{
MessageBeep(1);
}
}
AliR.
Post by Joseph M. Newcomer
To get WM_TIMER messages, yes. This trick is actually pretty common; for example,
CAsyncSocket creates a dummy window to handle callback notifications, which are sent as
window messages; SAPI creates a dummy window to handle Text-to-Speech
notifications; and I
knew about the trick years ago. Any generic derived-direct-from-CWnd class will do
nicely.
Timer callbacks will work without a window, but overall they suck badly, because there is
no user-specified data object passed in (as I explain to my students, the callback
function has four parameters, three of which are totally useless and one
of which is never
used anyway, so why do we have four useless parameters? Bad design. You can't really
make a thread-safe callback without a lot of effort; __declspec(thread) helps, or direct
use of Thread Local Storage.
My "timer-in-a-CDocument" essay shows how this can be done in a way that lets a CDocument
maintain an internal timer which has nothing to do with a view, which is useful if the
"document" is virtual and is implemented at the other end of a communication wire (RS232,
RS485, USB, etc.) and the timeouts deal with the document's communication
with its virtual
implementation and should not be visible to the views.
joe
Post by AliR (VC++ MVP)
Joe you must be kidding, he needs a window in his UI thread just use timers?
::SetTimer works just fine in CWinThread. All you have to do is set a
ON_THREAD_MESSAGE(WM_TIMER,OnTimer) in your message map.
AliR.
Post by Joseph M. Newcomer
Is it a UI thread? Is the timer associated with a window in the UI
thread? (You may have
to create an invisible top-level window to get this to work...)
joe
Post by Stefano
Post by Joseph M. Newcomer
Why not do something very simple: ON_WM_TIMER()?
I tryed with ON_WM_TIMER but it's never called.
Post by Joseph M. Newcomer
You do not need a g_iTmer, g_hWnd, or g_iMessage here, if this were a handler for
WM_TIMER. Get rid of them. I can't even imagine why they are needed at
all, since they
coudl be member variables of the CWatchDog class.
Yes you are right. I copyed and paste the code from the the callback.
Joseph M. Newcomer [MVP]
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
David Wilkinson
2008-03-14 18:02:32 UTC
Permalink
Post by Joseph M. Newcomer
Learn something new every day...
joe
Joe:

Yes, I didn't know you could do this either. Where is it documented?
--
David Wilkinson
Visual C++ MVP
Stefano
2008-03-14 15:13:46 UTC
Permalink
Post by AliR (VC++ MVP)
BOOL CMyThread::InitInstance()
{
   m_TimerID = SetTimer(NULL,0,5000,NULL);
   return TRUE;
In this way works. But If I add a method

void CMyThread::Monitor(...)
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
}

and I call from my dialog, the timer was set but OnTimer was never
called.
(Sorry I'm new to thread maybe Im doing something wrong)
Scott McPhillips [MVP]
2008-03-14 16:07:05 UTC
Permalink
Post by AliR (VC++ MVP)
BOOL CMyThread::InitInstance()
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
return TRUE;
In this way works. But If I add a method

void CMyThread::Monitor(...)
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
}

and I call from my dialog, the timer was set but OnTimer was never
called.


If you call it from your dialog then you are calling from the main thread
and the timer message will arrive in the main thread, not in the secondary
thread.
--
Scott McPhillips [VC++ MVP]
AliR (VC++ MVP)
2008-03-14 16:36:10 UTC
Permalink
If you want to set a timer in the thread from a dialog in your main thread,
then you will have to post a user defined message to the thread to tell it
to set the timer. You can't just call a method in your thread. As Scott
has already pointed out it will be setting the timer for the calling thread
(which is the main thread)


void CMyDialog::SetTimerInThread()
{
m_pThread->PostThreadMessage(WM_SETTIMER,0,0);
or
::PostThreadMessage(ThreadID,WM_SETTIMER,0,0);
}

//in message map of thread
ON_THREAD_MESSAGE(WM_SETTIMER,OnSetTimer)

void CMyThread::OnSetTimer(WPARAM,LPARAM)
{
SetTimer(NULL,1000,0,NULL);
}

AliR.
Post by AliR (VC++ MVP)
BOOL CMyThread::InitInstance()
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
return TRUE;
In this way works. But If I add a method

void CMyThread::Monitor(...)
{
m_TimerID = SetTimer(NULL,0,5000,NULL);
}

and I call from my dialog, the timer was set but OnTimer was never
called.
(Sorry I'm new to thread maybe Im doing something wrong)
Stefano
2008-03-15 11:24:10 UTC
Permalink
Post by AliR (VC++ MVP)
If you want to set a timer in the thread from a dialog in your main thread,
then you will have to post a user defined message to the thread to tell it
to set the timer.  You can't just call a method in your thread.  As Scott
has already pointed out it will be setting the timer for the calling thread
(which is the main thread)
I call the CMyThread::Monitor method from my dialog. Does this mean
than the timer will be associate to the main dialog ?
Can I define a method, inside the thread, to start the timer ?

void CMyThread::Monitor(...)
{
   m_TimerID = SetTimer(NULL,0,5000,NULL);

}

void CMyDialog::OnEvent()
{
m_pThread->Monitor();
}
David Wilkinson
2008-03-15 12:53:51 UTC
Permalink
Post by Stefano
I call the CMyThread::Monitor method from my dialog. Does this mean
than the timer will be associate to the main dialog ?
Can I define a method, inside the thread, to start the timer ?
void CMyThread::Monitor(...)
{
� �m_TimerID = SetTimer(NULL,0,5000,NULL);
}
void CMyDialog::OnEvent()
{
m_pThread->Monitor();
}
Stefano:

You need to understand that the thread context in which a function runs does not
depend on what class it is in, but rather where it is called from. C++ itself
knows nothing about threads.

Your CMyThread::Monitor() method is running in the main thread.
--
David Wilkinson
Visual C++ MVP
Scott McPhillips [MVP]
2008-03-15 14:34:03 UTC
Permalink
Post by Stefano
I call the CMyThread::Monitor method from my dialog. Does this mean
than the timer will be associate to the main dialog ?
Can I define a method, inside the thread, to start the timer ?
Yes, the timer will be associated with the main dialog because you called
from the main dialog. Threads cannot be "called." Putting the SetTimer
call inside your thread class does not make it run "inside the thread." (A
class is not a thread.)

You can put the SetTimer call in the thread's OnInitInstance, or in a thread
message handler.
--
Scott McPhillips [VC++ MVP]
Joseph M. Newcomer
2008-03-15 18:35:51 UTC
Permalink
The timer is associated with the thread that calls the SetTimer API. It does not care in
the slightest how these instructions have been wrapped by C++ classes, or the class names,
or anything else. So if you have a CWinThread class, it has code, and data. That's all
it has: code and data. The code is executed in the context of the thread that calls the
code. If you do AfxBeginThread, the code of the target function runs in the context of
the newly-created thread, and everything that code calls executes in the context of that
thread. If you do ANOTHER AfxBeginThread, the code of the target function runs in the
context of this second thread, and everything that code calls executes in the context of
that second thread. If you call a function from your main thread, the MFC class and the
name of the MFC class have NOTHING to do with the context in which the code is executed;
the code is executed in the context of the thread that calls it, and that is your main GUI
thread. Therefore, your timer messages will be sent to that thread, because that's what
you asked it to do.

Do not confuse superficial syntax with what is really going on. You can CALL it a "thread
class", DERIVE it from CWinThread, but the code is just code, and it always executes in
the context of the calling thread. No exception (note that cross-thread SendMessage does
not change this; what happens is that the sending thread is suspended, the thread which
receives the message is activated, and the code is executed in the context of the
receiving thread. SendMessage is *not* a "call", it is a request to deschedule one
thread and reschedule another. Thus the code is always executed in the context of the
calling thread, because the OS itself initiates the call of the receiving thread in the
context of the receiving thread.

Of course you can define a call within the thread. It can happen in the top-level thread
function, for example, or in a UI thread, in the InitInstance for that thread. Then the
code is called from the thread, and will impact the thread that called it.
Post by Stefano
Post by AliR (VC++ MVP)
If you want to set a timer in the thread from a dialog in your main thread,
then you will have to post a user defined message to the thread to tell it
to set the timer.  You can't just call a method in your thread.  As Scott
has already pointed out it will be setting the timer for the calling thread
(which is the main thread)
I call the CMyThread::Monitor method from my dialog. Does this mean
than the timer will be associate to the main dialog ?
Can I define a method, inside the thread, to start the timer ?
void CMyThread::Monitor(...)
{
   m_TimerID = SetTimer(NULL,0,5000,NULL);
}
void CMyDialog::OnEvent()
{
m_pThread->Monitor();
}
****
Yes, this is incorrect, for the reasons already stated. If the secondary thread is a UI
thread, the OnEvent code could PostThreadMessage to the UI secondary thread a notification
that causes the receiving thread to SetTimer, but this can only work if there is actually
an active message pump in the secondary thread. If it is a pure-computation thread in an
infinite loop, you have to be able to set a flag that says "please set the timer and clear
this flag", and this flag must be tested with reasonable frequency to be effective.
joe
*****
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Stefano
2008-03-18 11:22:06 UTC
Permalink
Thanks for your information. Now I understand why it doesn't work.
David Ching
2008-03-13 20:02:57 UTC
Permalink
Post by Joseph M. Newcomer
To get WM_TIMER messages, yes. This trick is actually pretty common; for example,
CAsyncSocket creates a dummy window to handle callback notifications, which are sent as
window messages; SAPI creates a dummy window to handle Text-to-Speech notifications; and I
knew about the trick years ago. Any generic derived-direct-from-CWnd class will do
nicely.
Timer callbacks will work without a window, but overall they suck badly, because there is
no user-specified data object passed in (as I explain to my students, the callback
function has four parameters, three of which are totally useless and one of which is never
used anyway, so why do we have four useless parameters? Bad design. You can't really
make a thread-safe callback without a lot of effort; __declspec(thread) helps, or direct
use of Thread Local Storage.
I think you misunderstood. AliR was promoting handling the WM_TIMER message
in the CWinThread message map. This means there is no HWND created for it.
And since it is a WM_TIMER message, the handler does have a this pointer, so
state can be preserved (in the CWinThread object).

He was not saying to use the SetTimer() variant which uses a callback
function.

-- David
Loading...