 |
|
 |
Hello, I included your source file in my app., TIMER starts but it does not fire my procedure:
I wrote:
BEGIN_MESSAGE_MAP(CDialogTerminale, CDialog)
//{{AFX_MSG_MAP(CDialogTerminale)
ON_BN_CLICKED(IDC_PROVA2, OnProva2)
//}}AFX_MSG_MAP
ON_WM_SERIAL(OnSerialMsg)
ON_COMMAND( IDC_DOCOMMAND, OnDoCommand )
END_MESSAGE_MAP()
void CDialogTerminale::OnDoCommand()
{
AfxMessageBox("GIANNI");
return;
}
in my .h file:
afx_msg void OnDoCommand();
timer runs but do not fire my routine OnDoCommand()
what I am doing wrong ?
thanks
Gianni
|
|
|
|
 |
|
 |
I'd have to see the code where you invoke the timer -
new CTimerOneShot( 2000, GetSafeHwnd(),
IDC_DOCOMMAND );
|
|
|
|
 |
|
 |
Dear Author,
I found it extremely handy while waiting for a small amount of time (say 10 millisec) and then trigger our function call.
This is what I invariably needed when I was playing around with CView::OnActivateView() and having two (or more) Views in my app. The problem context was - "Keep one View active always", i.e. if other Views try to become Active (user activates them by MouseActivate()), then (re)activate our _desired_ View.
Thank you very one for such a unique solution.
Regards and Best Wishes,
Yogesh Dhakad
|
|
|
|
 |
|
 |
This is still valuable info to me for learning C++. I don't need super accurate timing for my immediate need and I've spent the last 3 days trying to figure out the SetTimer Callback BS. Only to find a bunch of non static errors. This has been the hardest timer I have ever had to figure out. I've used other programming languages and it just seems ridiculous that one would have to go through this much work to get a simple timer function. Argh! So, yeah, this helps tremendously.
For the other commentor, Perhaps you could provide some information on how you impemented your thread based timer since you dropped SetTimer? I'd be iunterested to know how to do it that way as well. I did a search for it here on Code Project and didn't find anything too helpful yet.
Thanks again to both of you,
lanrosta
|
|
|
|
 |
|
 |
lanrosta wrote: spent the last 3 days trying to figure out the SetTimer Callback BS. Only to find a bunch of non static errors. This has been the hardest timer I have ever had to figure out. I've used other programming languages and it just seems ridiculous that one would have to go through this much work to get a simple timer function. Argh!
SetTimer is easy if you understand the limitations of "callbacks".
Every time the timer expires, the system invokes the function you provided to SetTimer. Simple enough. But if you need your function to interact with classes like MFC or whatever, you have to work a little harder.
In the easiest implementation, the callback function you write cannot access your Dialog CEdit windows, for example. Even if you initiate the timer by pressing a dialog button, when the function finally gets to run, it is NOT running it in the context of your classes.
The term "callback" is misleading. The func doesn't get called back into the environment you were in when you set the timer (as I once thought it should) but simply gets called. The term "back" merely means that a function you gave is given back to you... sort of.
In case you haven't gotten the simplest timer going, here's an example that rings the PC speaker bell every second until you press the Stop button. You should be able to get this going in your own MFC Dialog (or wherever).
#define MYTIMER 1
bool bRepeatable = true;
int iMilliseconds = 1000;
CEdit *textwin;
void CALLBACK MyTimer (
HWND hWnd,
UINT nMsg,
UINT nIDEvent,
DWORD dwTime);
...
bool CSetTimerDlg::OnInitDialog()
{
...
textwin = &CEdit_myDialogEditWindow;
}
void CSetTimerDlg::OnBnClickedStartTimer()
{
SetTimer(MYTIMER, iMilliseconds, MyTimer);
}
void CSetTimerDlg::OnBnClickedStopTimer()
{
KillTimer(MYTIMER);
}
void CSetTimerDlg::OnBnClickedCheckRepeatable()
{
bRepeatable = (BST_CHECKED ==
CheckButton_Repeatable.GetCheck());
}
void CALLBACK MyTimer (
HWND hWnd,
UINT nMsg,
UINT nIDEvent,
DWORD dwTime)
{
Beep(300, 30);
if (!bRepeatable) {
KillTimer(hWnd, nIDEvent);
textwin->SetWindowText("stopped");
}
}
The most common problem is arranging for the independently running callback function to be able to access whatever class members it's supposed to work with. Note the global pointer to the Dialog CEdit text window (initialized during the Dialog init function). There may be better solutions, but this works for simple access. Of course, you have to be careful whenever you cheat this way. The more your callback needs to work with a class, the more you punch holes in the class.
Finally, it should be noted that you can't get less than about a 15-millisecond granularity with WM_TIMER timers under the most perfect circumstances. If you need better accuracy you can use a multimedia timer, but it's not generally worth the trouble.
|
|
|
|
 |
|
 |
Thanks RStudio. Yep, I am finding these pitfalls along the way as well. Your info certainly helps me realize I am not retarded when I figure out the same situations are occuring with me. I* am not using MFC as I am building a VST (http://www.steinberg.de) and these are .dll's that get loaded in a VST Host program. So, this makes it even more difficult for me to figure out when I have no dialog or direct CWnd to apply it to. But basically, I got a version working, but am stuck at the 'access whatever class members it's supposed to work with' part. Would a global pointer to any other class (other than Dlg work as well).?
Thanks again,
lanrosta
lanrosta
|
|
|
|
 |
|
 |
lanrosta wrote: stuck at the 'access whatever class members it's supposed to work with' part. Would a global pointer to any other class (other than Dlg work as well).?
I don't know anything about VST but they look like great products. If you're able to hook into their API, maybe they already provide a timing mechanism you could use. Remember the standard SetTimer (which uses WM_TIMER) will likely be off by as much as 15ms.
But sure, you should be able to set global pointers to any "public" class variables, but remember they have to remain usable when you go to use them... If you create an object local to a function (not with "new") and set a global variable to point at one of its members, and the object is destroyed before your timer goes off (eg, you leave the function before the timer goes off), your pointer will hold a memory address to something that no longer exists.
Better yet you might set the global variable to point to the class object:
ThatClass *gThat = (ThatClass *)NULL;
somefunc() {
gThat = new ThatClass();
}
Then every function (even a callback function run by the timer) should be able to test that variable to see if it's NULL, and if it's not NULL it should [probably] be able to access the class.
if (gThat)
gThat->DoSomething();
If you've been trying this and not getting anywhere, perhaps the class you're trying to work with relies on other things that are no longer in scope when your timer tries to use the pointer. Or perhaps you need to work harder to load something from the DLL.
Once you have a global pointer to access your class object, you have to care whether or not the class object still exists when you want to access it through the global pointer, and whether or not the object is in fact capable of doing what you want it to do (increase error checking). The biggest fear is that your object has disappeared while you still hold a pointer in your global variable. Make sure that you NULL the variable when you are eventually finished with it.
if (gThat) {
delete gThat;
gThat = NULL;
}
Digging into DLLs requires more help than I can provide, but if the code expects to be accessed, global pointers to class objects like this (created with "new") might well resolve your situation.
CodeProject probably has a ton of info on loading DLLs. Great place we have here.
Best luck.
-- modified at 17:45 Thursday 26th January, 2006
|
|
|
|
 |
|
 |
If you look at the code in the article, the "this" pointer is passed in as the timerID, and recast in the callback. This is much safer than a global "this" pointer, if more than one timers are active.
|
|
|
|
 |
|
 |
With multiple timers trying to access the same global object, potentially at the same time, I agree it would probably be much safer to work out a specialized one-shot object for the timer to call and destroy, and your example has much to offer. Thanks for sharing this.
I don't know what Ianrosta is trying to do, but it sounded like he needed some clarification for the underlying timer callback scheme, so I offered a simplistic situation to cheer him up. It would certainly be a good exercise to go through your example and understand what's going on there, especially regarding the passing of the "this" pointer, but it seems a little complicated for someone learning C++. Good challenge though.
For my own purposes I sometimes want a timer that will frequently interact with a long-term object already in existence, in a context where I expect no conflict and want a fast response with minimal overhead. Invoking a new class or subclass for each of those calls seems like overkill to me, and the global pointer works fine. But I confess most of my code uses C++ as if it was "C with some objects" and apparently I don't use object-oriented methods as often as you might.
Interesting example. Thanks for the pointers.
|
|
|
|
 |
|
 |
I agree completely. If your application uses lots of timer intensive stuff, a long term timer object is the answer. But if you just want a delay for some reason, the one shot class is a good solution. The thing that appealed to me about this implementation is that it is a write once use often class. Because it posts/sends a message to the parent, no need to derive classes for different behaviour. Just post a different message/command ID and you are done. This also solves the usual SetTimer issues of callback function declarations ( static, global this pointers, etc )
But, many, many ways to solve this common problem. This is just one approach.
Steve
|
|
|
|
 |
|
 |
WM_TIMER messages are the lowest priority message in Windows, and they aren't guaranteed to be sent. I think a worker thread would be a better choice.
------- sig starts
"I've heard some drivers saying, 'We're going too fast here...'. If you're not here to race, go the hell home - don't come here and grumble about going too fast. Why don't you tie a kerosene rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
Hi John,
This implementation does not explicitly use WM_TIMER messages. It uses a callback function as provided by the Win32 SetTimer function. ( Perhaps at the OS level, WM_TIMER is used to implement this ). The callback then posts a user defined message to the target window's message queue. This class could be extended to include SendMessage variants if synchronous execution is desired.
Steve
|
|
|
|
 |
|
 |
If you're using ::SetTimer and ::KillTimer, you're using the WM_TIMER mechanism. We used to do the WM_TIMER thing, but about six years ago, we changed over to a worker thread class and our program became instantly more consistent as far as the timed events we were monitoring were concerned.
I'm not trying to discount your efforts here, just providing an alternative outlook.
Maybe you could extend your article to include a one-shot timer class that was a worker thread.
------- sig starts
"I've heard some drivers saying, 'We're going too fast here...'. If you're not here to race, go the hell home - don't come here and grumble about going too fast. Why don't you tie a kerosene rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
You are correct - after your last post I thought about it, and it would be a good improvement. Maybe down the road when I get some free time, I'll update the article. The current implementation works for my application, so this goes on the back burner!
Steve
|
|
|
|
 |
|
 |
That sounds interesting, but how do you get a worker thread to run some code at a specified time in the future without using the WM_TIMER mechanism?
|
|
|
|
 |
|
 |
That is a good question - I hadn't given it much thought. Maybe John can tell us how he did it.
You could certainly create a timer effect in the worker thread using Sleep, SleepEx, or WaitForSingleObject with the desired timeout value.
|
|
|
|
 |
|
 |
How about calling CreateWaitableTimer()/SetWaitableTimer() and WaitFor*() in a worker thread, started just after the timer has been triggered?
|
|
|
|
 |