|
Hi,
I can build the program fine but I'm getting ASSERTION errors
when I try to run the program. The first error occurs in the
following module:
BOOL CDateTimeCtrl::SetTime(const COleDateTime& timeNew)
{
BOOL bRetVal = FALSE;
// make sure the time isn't invalid
ASSERT(timeNew.GetStatus() != COleDateTime::invalid);
ASSERT(::IsWindow(m_hWnd));
SYSTEMTIME sysTime;
WPARAM wParam = GDT_NONE;
if (timeNew.GetStatus() == COleDateTime::valid &&
timeNew.GetAsSystemTime(sysTime))
{
wParam = GDT_VALID;
}
bRetVal = (BOOL) ::SendMessage(m_hWnd,
DTM_SETSYSTEMTIME, wParam, (LPARAM) &sysTime);
return bRetVal;
}
at the first ASSERT statement. Do you have any idea as to why this is happening.
By the way, this looks like a fine piece of work.
Ken
|
|
|
|
|
Hi Ken,
KenHickey wrote: at the first ASSERT statement. Do you have any idea as to why this is happening.
It looks like you're calling SetTime with a type of parameter I'm not familiar with. SetTime takes 3 types of inputs: a "CTime *", "COleDateTime&" and "LPSYSTEMTIME". The code snippet you put into the question shows that you are passing a COleDateTime&. That's something I've never used before. Is that the intention, to pass a COLeDateTime&? If you're using an LPSYSTEMTIME then you might need to cast it like this: date_time_ctrl->SetTime((LPSYSTEMTIME) NewTime&); // for example...
But it looks to me like SetTime doesn't see a valid COleDateTime in the parameter of the call. Can you set a breakpoint at SetTime and see what it is that gets passed?
KenHickey wrote: By the way, this looks like a fine piece of work.
Thanks for the compliment! Wish it were better, though. I have some new code that fixes some problems but haven't updated the docs yet...but email me if you want the latest code and it will fix some probs too.
Regards,
Dave
|
|
|
|
|
Dave,
I'm not quite sure what's going on here. I tried to comment it out, the first ASSERT for example, but the program still "hung" at that point. I take it that in debug mode, the compiler just ignores the comments around any ASSERT statements???
I used a breakpoint there and the value of "timeNew.m_dt" is a large negative number which doesn't make much sense. The status component of the variable is "invalid" which does make sense due to the time value passed.
I never meddled with the SetTime functions. In other words, the COleDateTime& parameter passed to SetTime was in the original code.
It works fine in release mode but I'm not sure why the variable 'timeNew' would have a different value in debug mode,
if this is actually what's happening.
Ken
|
|
|
|
|
I wish I knew how to help. It's not clear to me why you're having problems. Maybe VC++ 6.0 won't build this project. I'm not using the COleDateTime class in my example code so that doesn't make sense why you're mentioning it.
|
|
|
|
|
Hi Dave,
Yes, you may be right here since I'm having problems using class wizard to generate a proper CLW file. I can't examine any of the classes with the wizard even though the classes exist in the workspace.
Sorry about the COleDateTime class misconception. The ASSERT error occurred in one of the MFC library files... "winctrl5.cpp" so it's not your code at all.
I will try to sort out some of this within the next few days. I'll let you know if I make any progress.
Thanx for your help.
Ken
|
|
|
|
|
Hi Again,
I think I have this nearly figured out.
First of all, I changed up the 'ClockTestDlg'class definitions
such that the class wizard recognized it and now I have a correct version of the CLW file that VC++ 6.0 can read.
Secondly, the error regarding the 'SetTime' function is in the 'Update' function in the following line:
m_Date_Until.SetTime(m_RepeatParms.tRepeatUntil.ft);
When I commented this out, the debug version would run fine. Anyway, there must be some problem with the sizeof this variable or something like that. It should be fairly easy to figure out now (I hope).
Anyway, this is good. I now may have a version that is completely compatible with VC++ 6.0 service pack 4.
Ken
|
|
|
|
|
Ken, I have a long-shot theory about the assertion in the code that handles SetTime for the DateTimePicker: Could it be a need to load the latest Platform SDK? I'm wondering if that will fix the error because the DateTimePicker control isn't specific to Visual Studio 2003 AFAIK.
Also I had problems with the control in the resource editor where the control kept changing back to its default mode (or style).
Just guessing, though....
BTW: I have a newer version of the sample apps and CAlarmClock if you want. I found some problems a while back and fixed them but due to the extra functions I added and haven't tested eerything, I haven't posted it on CodeProject yet but will eventually do that when I have time...whenever that is.
Regards,
Dave
|
|
|
|
|
Hi Dave,
Not being too familiar with the date/time functions of MFC (but I'm sure that I will have to be from this point onward) I did a bit of digging and discovered the following wrt VISUAL C++ 6.0
The SetTime function requires 3 possible inputs (1) a reference to a COleDateTime object (2) a pointer to a CTime object (3) a SYSTEMTIME object.
Since your program has as input a FILETIME object we have to convert this to one of the previous 3. Hence, the simple solution is
m_Date_Until.SetTime(&CTime(m_RepeatParms.tRepeatUntil.ft));
which just converts the input to a CTime object and passes the address of this to the SetTime function.
I haven't checked out the lastest SDK but I will. Hope this is a tiny bit informative. I take it that in ver 7.0 the setTime function may actually have as input a FILETIME structure object.
Ken
|
|
|
|
|
KenHickey wrote: Not being too familiar with the date/time functions of MFC
Hi Ken,
I guess I'm not very familiar with this control either. I didn't notice that it was asking for a LPSYSTEMTIME instead of an LPFILETIME. Now I can't understand why my sample doesn't assert. You found a bug in my code.....(now I finally realized) but for some reason, mine never asserts. I should've converted the FILETIME into a SYSTEMTIME. I wonder if MS has some undocumented code that allows this thing to accept an LPFILETIME too. It sure acts that way.
Thanks for pointing out this bug to me and sorry for my confusion. This is the first time I've worked with CDateTimeCtrl too so I made an assumtion that it takes FILETIME and now I'm humbled .
Thanks,
Dave
|
|
|
|
|
Hi Ken,
DaveeCom wrote: I have some new code that fixes some problems but haven't updated the docs yet...but email me if you want the latest code and it will fix some probs too.
I'd like to get your changes, but I tried to email you and the CodeProject form said that it can't email to your address. Could you please post your updates to this article? And/or email me the latest code?
I also posted another new thread to this article a couple days ago, but I'm not sure if you got notification.
Thanks again for sharing such a great class!
-Ed
--
Edward Livingston
(aka ExtraLean)
--
"I still maintain that seeing a nipple is far less disturbing than seeing someone get their brains blown out." -- Chris Maunder
|
|
|
|
|
Hi, I’m trying to create a new project using AlarmClock.h and .cpp. I created an empty project in Visual C++ 6.0, added both files, and created a main function, NOTHING ELSE. I also followed your instructions in setting the project properties.
When trying to compile, I got a LOT of errors, every one of them relating to the UFT union. For instance, with the following instruction
u2.ll = u1.ll + ONE_SECOND;
I get the following erros:
C:\...\AlarmClock.cpp(78) : error C2059: syntax error : 'bad suffix on number'
C:\...\AlarmClock.cpp(78) : error C2146: syntax error : missing ')' before identifier 'l'
C:\...\AlarmClock.cpp(78) : error C2059: syntax error : ')'
It repeats these errors EVERY TIME that there’s an instruction using .ll.
Any ideas ?
Thanks,
Leo.
|
|
|
|
|
Hi Leo,
It's because of the ll on the end of the number in the #define of the large value ONE_SECOND. I have to go to work now but when I get home I'll see if I can learn how to make it compile with the earlier compiler. There's probably someone that knows the answer already but I'll be on top of it in a few hours. I used ll to indicate that the number is a 64 bit integer. If you can define a 64 bit constant then you can replace the ll with something else, but I don't yet know what to change it to.
Will be back in a few hours to start to look at it. I also have an updated module and can email it to you if you want. I haven't taken the time to document the new changes yet but the new updated clock is in heavy use in one of my apps as a short duration timer and I've seen no failures in hundreds of hours of solid operation.
Will be back to try to find the answer on how to build on pre-64 bit compilers.
Regards,
Dave
|
|
|
|
|
Try changing the ll to i64 and let me know if that fixes it.
#define ONE_MILLISECOND (10000i64)
#define ONE_SECOND (10000000i64)
|
|
|
|
|
Hi Dave,
thanks for your answer, it worked!
I also had to change the line
bRepeatUntil = pThis->m_RepeatParms.tRepeatUntil.ll != 0ll;
in AlarmClock.cpp to:
bRepeatUntil = pThis->m_RepeatParms.tRepeatUntil.ll != 0i64;
I have absolutely no idea what these letters at the end of constants numbers mean, but it worked
Leo.
|
|
|
|
|
Hi Leo,
Thanks for the feedback. You gave me an idea: I'm going to test to see if the define called 'll' exists and if not, I'll issue -
#define ll i64 and that should make it more portable across VC versions.
I have absolutely no idea what these letters at the end of constants numbers mean, but it worked
The reason this is necessary is to force the compiler to generate code that correctly operates on these large integers as 64 bits. If you don't say ll (or i64) then the code will ignore the upper 32 bits. It's necessary to handle numbers this large if you're going to compare binary time units like a FILETIME, even though a FILETIME is broken into two parts by the structure. 'll ' stands for LONGLONG I think.
Regards,
Dave
|
|
|
|
|
How about making the AlarmClock class serializable?
|
|
|
|
|
It's possible but then there would be a dependancy on MFC. My intention was to not make it dependant on MFC so that all Win32 c++ apps could use the class.
But this is source code. People can do their own thing to it if they want.
1: Derive it from CObject.
2: Add the macros IMPLEMENT_SERIAL and DECLARE_SERIAL
3: Override virtual function Serialize.
After restoring from an archive you'll need to put the clock into the proper state after reading and interpreting the vars. That's about all there is to it.
-- modified at 21:24 Friday 24th March, 2006
Forgot to say: If you want to make it serializable I'll be glad to help if you need it.
|
|
|
|
|
I am thinking of changing the way the alarm is scheduled (or maybe I can add more member funtions).
Right now, in order to use the repeat alarms, the programmer needs to set one alarm then add a repeat type. This was the way I implemented the class but I hadn't really thought it out too well at the time I wrote it. Using it this way is not that easy and it was supposed to be easy.
The problem with the way this thing is used right now is that an alarm has to be scheduled in order to use the repeat alarms. The burden is currently on the programmer/user because they have to call SetAlarm with a valid alarm time that hasn't expired yet. But it's not simple to make use of the class if this is necessary. It's a REAL PAIN.
So it needs to be changed.
In order to make the class easier to use, I need to add more functions, to allow the user to schedule a repeat alarm and activate the first of the repeat alarms automatically....by calling a single function.
I'm going to add a new set of function calls that have a name like ScheduleRepeatAlarm(with some parameters from SetAlarm and some from SetRepeat). By doing this, I can make it so that a single function call schedules and arms the clock.
- My idea for how to fix it is to add more functions to replace the existing ones. SetAlarm and SetRepeatxxx can be avoided and ScheduleRepeatAlarm() can be used instead.
An example with paremeters could be like this:
BOOL ScheduleRepeatAlarm_Interval(long nCount, HWND hWndAlert, SYSTEMTIME tmExpires, int dd, int hh, int mm, int ss, int ms);
BOOL ScheduleRepeatAlarm_Monthly(long nCount, HWND hWndAlert, SYSTEMTIME tmExpires, int DayOfMonth);
BOOL ScheduleRepeatAlarm_Weekly(long nCount, HWND hWndAlert, SYSTEMTIME tmExpires, BOOL *bWeekdays[7]);
Suggestions are welcome.
Dave
|
|
|
|
|
I was thinking, pondering - why dont you take a repeat 'specification' as text, for example, borrowing grammar from rfc2445 recurrence clauses .. then you dont need so many buttons etc
its probably harder in that you have to parse the grammar and map it back to your own methods, but its flexible (you could start with a subset even)
just an idea .. no criticism
'g'
added .. in fact, you could specify the whole alarm as text, then you wouldnt necessarily need a ui - you could then store alarms in a file
-- modified at 2:03 Friday 24th March, 2006
|
|
|
|
|
There are advantages to parsing text but I don't want to do that much work. I appreciate the input. It doesn't feel like a criticism but I can say that I'm ready for more criticism after the way I presented this thing.
It turns out that after publishing this class here, I'm finally getting around to trying to use it myself (as I originally intended) and it's too hard to use and make the human have to tell it the initial date/time. I should've waited until I was completely happy with it before publishing it.
Now that it's in the public domain, I'd like to fix the poor interface one more time and simply create sensible public member functions that do a lot without the programmer/user having to do much work. It shouldn't have to be necessary to call two functions in order to shedule and activate a repeated alarm....as it is right now.
|
|
|
|
|
I always find it interesting, that despite the language capabilities offered in C++, member functions continue to reflect C++'s heritage in C. Why not use overloading through the use of a single function name (ScheduleRepeatAlarm) and allow signature uniqueness via the argument list?
BOOL ScheduleRepeatAlarm(long nCount, HWND hWndAlert, SYSTEMTIME tmExpires, int dd, int hh, int mm, int ss, int ms);
BOOL ScheduleRepeatAlarm(long nCount, HWND hWndAlert, SYSTEMTIME tmExpires, int DayOfMonth);
BOOL ScheduleRepeatAlarm(long nCount, HWND hWndAlert, SYSTEMTIME tmExpires, BOOL *bWeekdays[7]);
Just a suggestion! This is by no means limited to your class, which I find interesting and worth further evaluation.
|
|
|
|
|
I absolutely agree. It can be just one function name with different parms.
But...what are the vital parms and which ones are not needed? That is the question I am ponding now...after the fact...
Each of these functions needs (IMHO)
- The time of day for the recurring event to happen at (only if it's daily, weeky or monthly).
- The count of recurring events (or an expiration date).
- The recurring info (interval period, weekdays, day of month)
- The means of signalling the caller (HWND, Event or Callback)
Daily time is only needed for daily, weekly, monthly.
Count can be zero (for forever).
Expiration date can be zero (to ignore exp-date and use count value instead).
This is what I'm pondering right now about what parameters are needed in a single (do everything) repeat-alarm-scheduling function.
|
|
|
|
|
Here's my proposal for a simpler way to use repeat alarms. I created another set of member functions to schedule and start the repeated alarm in a single call. I tried to think of everything that would be practical but I could've overlooked something.
(Sorry for the poor formatting. The CP web server won't keep the spacing the way I wanted it to look. It seems to remove most blanks that I added.)
The interval scheduling function currently looks like this:
BOOL SetRepeatAlarm(LPALARMCB lpCB, DWORD dwUser,
int iDD, int iHH, int iMM, int iSS, int iMS, // Alarm triggers every ...
BOOL bSyncDD, // 1st alarm starts at top of next hour (XX:00:00)
BOOL bSyncHH, // 1st alarm starts at top of next day (00:00:00)
long nCount = 0, // Alarm expires after nn counts or forever if zero.
LPSYSTEMTIME lpExpDate = NULL); // Alarm expires after this date & time has passed.
// (overrides nCount). Ignored if NULL or values in struct=0.
The monthly repeat scheduling function looks like:
BOOL SetRepeatAlarm(LPALARMCB lpCB, DWORD dwUser,
LPSYSTEMTIME lpTimeOfDay, // Time of alarm (only wHour, wMinute and wSecond are used)
int nDayOfMonth, // 1 to 31
long nCount = 0, LPSYSTEMTIME lpExpDate = NULL);
The weekly or daily:
BOOL SetRepeatAlarm(LPALARMCB lpCB, DWORD dwUser,
LPSYSTEMTIME lpTimeOfDay, // Time of alarm (only wHour, wMinute and wSecond are used)
BOOL *bWeekdays[7], // {F, T, T, T, F, F, F} = Day of Week starting at Sunday.
// Example shows Mon, Tues and Wed are set. F=FALSE, T=TRUE
long nCount = 0, LPSYSTEMTIME lpExpDate = NULL);
-------------
Recurring intervals not based on daily or weekly or monthly:
Regarding the first of the three example functions (used for non-daily recurring events) -
I can't imagine every possible way of using a recurring alarm, but I can think of how *I* would want to use it. I want to use it like a VCR clock calendar to control a recorder. There's probably ways of making the capability much more versatile (and complex) but it escapes my why someone would want to synchronize an hourly event to 20 minutes after every hour. So I left that capability out. But you can schedule a recurring alarm that starts at the top of the next hour or midnight of the next day. If you really need to set a recurring alarm that is hourly but occurs on the 1/2 hour of every hour, then you can still use the SetAlarm and SetRepeat functions to accomplish that. But for simplicity, I decided to make the single function calls less versatile but easier to use. So I added two flags: (bSyncHH and bSyncDD) to allow the user to sync an interval alarm to the hour or the day, (respectively).
The other recurring types (monthly, weekly) don't need sync because they have a way to specify the daily time: (lpTimeOfDay). For a daily, weekly or monthly alarm, use lpTimeOfDay to specify the alarm time for each day. Only the SYSTEMTIME::wHour, wMinute and wSecond values are used. The rest are ignored.
If someone wants to set a recurring alarm on the 1/2 hour point of every hour, this is how I recommend doing that:
// Declared in a place that won't lose scope -
CAlarmClock m_Clock;
// In your code to set the example recurring event to the bottom of every hour using the existing function set: (untested example code)
SYSTEMTIME tTime;
GetLocalTime(&tTime); // Get current time so we have a starting point
if (tTime.wMinute > 30)
{
tTime.wHour++;
// ... Need to handle all overflows... good luck
}
tTime.wMinute = 30;
m_Clock.SetRepeat_Interval(0, 0, 1, 0, 0, 0);
m_Clock.SetAlarm(tTime, hWnd, 0);
// From this point, you'll start getting the windows message every hour on the 1/2 mark.
Footnote: Maybe there's something important that I overlooked. If you have a concern about how I propose to define this, pls reply so I can avoid adding something that's based on flawed logic. It won't hurt my feelings. That's why I'm here asking so I can get it right,
I'll wait a few days before writing the code in case someone wants to reply to this thread.
-- modified at 16:35 Saturday 25th March, 2006
|
|
|
|
|
I'm going to add the following two functions only, in order to add a simpler way to use repeat alarms. The interval (DD, HH, MM, SS, MS) version of the function will not be needed AFAIK:
BOOL SetRepeatAlarm(LPALARMCB lpCB, DWORD dwUser,
LPSYSTEMTIME lpTimeOfDay,
int nDayOfMonth,
long nCount = 0, LPSYSTEMTIME lpExpDate = NULL);
BOOL SetRepeatAlarm(LPALARMCB lpCB, DWORD dwUser,
LPSYSTEMTIME lpTimeOfDay,
BOOL *bWeekdays[7],
long nCount = 0, LPSYSTEMTIME lpExpDate = NULL);
After consideration, I can't think that the 1st of the proposed functions from the previous post would be very useful. So it's only going to be the 2 functions I listed here that will be added to support easier repeat schedules. If someone needs a simple daily repeat alarm, they can use the weekly (above) with all days set to TRUE. If they want to make an hourly repeat alarm, it's not that big a deal to use two function calls (SetAlarm followed by SetRepeat_Interval ).
This will be the final update for this class except that there will probably still be some bug fixes if necessary, afterwards. And one last thing: I think it's best to rename the SetRepeat_Interval , SetRepeat_Monthly , etc. to just SetRepeat in order to make use of c++ overloading. That will break anyone's code if they're already using this class. Just need to rename their "SetRepeat_Monthly " to SetRepeat and their code should compile aggain. Hope no one minds this approach to clean up the names. It's better to rename them and keep everything consistent.
|
|
|
|
|
Just found a new bug - When repeating an alarm using the repeat by interval type, repeat nn days is being ignored.
The good news is that it's been fixed. But until the upload is processed, please change AlarmClock.cpp. Change the CalculateNewAlarmInterval member function to look like this:
-----------------------------
void CAlarmClock::CalculateNewAlarmInterval(CAlarmClock* pThis)
{
UFT u1, u2;
_ASSERT(pThis);
u1.ll = pThis->m_RepeatParms.dd * ONE_DAY;
u1.ll += pThis->m_RepeatParms.hh * ONE_HOUR;
u1.ll += pThis->m_RepeatParms.mm * ONE_MINUTE;
u1.ll += pThis->m_RepeatParms.ss * ONE_SECOND;
u1.ll += pThis->m_RepeatParms.ms * ONE_MILLISECOND;
u2.ft = pThis->m_AlarmTime; // Get old alarm
u2.ll += u1.ll; // Add offset to make new alarm.
pThis->m_AlarmTime = u2.ft;
}
-----------------------------
Sorry for letting this bug slip by me earlier.
|
|
|
|
|