Friday, October 26, 2007
SystemState StateChanged Subscription Must Occur on UI Thread?
The Microsoft.WindowsMobile.Status.SystemState class offers a great way to track a number of interesting and useful device system states (e.g., incoming phone calls, battery strength, etc.). However, recently I observed that the SystemState.Changed += new ChangeEventHandler(OnSystemStateChanged) subscription must be made from the UI thread. Has anyone else run into this? I attempted to write a console app that uses many of these system state notifications; however, none of the events were actually occurring. Once I switched to a form-based application, everything worked great.
Is this documented somewhere? If so, I couldn't find it. I am programming in .NET CF 2 for Windows Mobile 5.0 devices.
Chris Tacke responded with,
Most of those events come from windows messages being posted at a low level (FileSystemWatcher works int he same way too). If you never call Application.Run, no message pump is ever created, so no windows messages are ever dispatched.
I don't think I've ever seen it documented, but if you walk through the aygshell source code in Platform Builder (which a lot of these come from eventually), you'll see that's how they work. I agree that it's a poor choice, but it's how it was implemented. The workaround is a very long, tedious process (again, we've done it for file system notifications) of reimplementing the entire listening system, creating a hidden window for your app or implementing a Run() that doesn't take a Form (like we did in the SDF).
Tuesday, August 29, 2006
Appointment ListChanged Event
Has anyone been able to receive an event when you've registered to receive events from ListChanged?
Here is the code I'm using (I'm using VS 2005 RC1 with CF 1.0):
OutlookSession outlook = new OutlookSession();
outlook.Appointments.Items.ListChanged += new
ListChangedEventHandler(Items_ListChanged);
This code runs just fine, but changing the calendar doesn't fire this event in my application. I've searched the net, but haven't found anyone who appears to have used it.
Peter Foot, from InTheHand fame, then responded:
I haven't checked, but it's possible that the events in Microsoft.WindowsMobile.PocketOutlook only fire when the changes are made through these APIs not when changed from another application...
The original poster, however, corrected Peter with information from the MSDN docs:
Here's what the docs say:
"Occurs when either the collection changes, or when an individual PIM item in the collection changes."
Additionally, the MSDN article "What's New for Developers in Windows Mobile 5.0" says that you can track changes made to Pocket Outlook folders using managed code and gives an example, so it sounds like it should show changes made by other applications. As far as I can tell, that's the whole idea, in fact.
But I can't get it to trigger any events.
SystemState CalendarAppointment Events Seem Very Broken
For the most part, I've had good experiences with the new Microsoft.WindowsMobile.Status.SystemState infrastructure in .NET CF 2.0. However, recently I've run up against numerous issues with the CalendarAppointment* and CalendarNextAppointment* events. Incidentally, I've tested this on the I-Mate KJAM Pocket PC Phone as well as the Cingular 2125, T-Mobile SDA, and the I-Mate SP5m SmartPhones. It shouldn't be too hard to repo what I'm describing on your own device.
I will describe the problems in descending importance:
1. The SystemState calendar related events do not properly notify subscribers when the calendar appointment state changes. My impression of these system states is that an event will be fired throughout the day as a user's calendar appointments occur. I would say that in more than 90% of the cases, no event is triggered. I haven't been able to diagnose exactly why this is. The problem I'm describing here seems relevant for all calendar related events. For example,
private SystemState _stateCalendarAppointmentBusyStatus;
private void Init()
{
_stateCalendarAppointmentBusyStatus = new
SystemState(SystemProperty.CalendarAppointmentBusyStatus, true);
_stateCalendarAppointmentBusyStatus.Changed += new
ChangeEventHandler(OnStateCalendarAppointmentBusyStatusChanged);
ChangeEventArgs args)
{
AppendText(SystemProperty.CalendarAppointmentBusyStatus,
(BusyStatus)args.NewValue);
{
_textBox.Text += systemProperty + ": " + newValue + "\r\n";
2. At this point, I thought perhaps I misunderstood what the current calendar appointment events did particularly because I seemed to get events when I would go into the calendar and modify the current appointment. However, this isn't the issue. I setup a polling sensor that would, instead of relying on the events, constantly poll the calendar related system states. You can do this because calling SystemState.CalendarAppointment, for example, returns the current value of that system state. However, what I found through polling was that this system state was not actually changing. To test this out, setup a "refresh" button in your GUI or run a windows Timer object to spit out the current state of the CalendarAppointment* system states. Then, add 5 or 6 test appointments to your calendar that will occur over the course of the next 10 minutes. You'll note that the states don't actually change with the calendar appointments. I've found that the CalendarAppointment* and CalendarNextAppointment* seem to be particularly troublesome while CalendarHomeScreenAppointment* stuff works a little more frequently (though these events are much less useful to us).
3. Going back to using the system state events. Interestingly, the following code will sometimes throw a NullReferenceException because the args object passed in is null. Perhaps this makes sense--if the current appointment is deleted the BusyStatus switches from a known state to a null state--however, this is underdocumented. An unsavvy programmer could easily fail to null check the ChangeEventArgs argument.
private void OnStateCalendarAppointmentBusyStatusChanged(object sender,
ChangeEventArgs args)
{
AppendText(SystemProperty.CalendarAppointmentBusyStatus,
(BusyStatus)args.NewValue);
UPDATE 09/11/2007: I received word from Microsoft today that this is indeed a bug in WM 5.0. Direct quote from the Microsoft employee, "I’ve confirmed the bug on WM 5.0, but it seems to be fixed in WM 6.0. That is, that the Notification Broker (and associated registry settings) don’t update properly when the calendar changes."
Friday, August 25, 2006
Issues with SystemState.Date
The first relates to SystemProperty.Date. Subscribing to the SystemProperty.Date results in a ChangedEventArgs object with the wrong value type, or, at least a value type that is inconsistent with documentation. Even the SystemState.Date type (which is of type System.DateTime) would leave you to believe that the ChangedEventArgs.NewValue object would be of type DateTime. In this case, however, it's actually a byte array of length 8. We believe this represents milliseconds in UTC time (8 bytes in C# is a long type). See this Date and Time article at MSDN for more on working with date and time. This typing issue results in a InvalidCastException in the following code.
private SystemState _stateDate;
cstr(){
_stateDate = new SystemState(SystemProperty.Date);
stateDate.Changed += new ChangeEventHandler(OnStateDateChanged);
}
private void OnStateDateChanged(object sender, ChangeEventArgs args){
//The code below will throw an exception as the args.NewValue type is actually
//a byte array of size 8! This is undocumented and unexplained.
DateTime date = ((DateTime)args.NewValue);
}
I have not comprehensively explored this issue. Other than the fact that the arg.NewValue is a byte array of length 8, I cannot elaborate. It is only conjecture that args.NewValue here is a timestamp in milliseconds at UTC. Regardless, it seems to contradict documentation and go against the paradigm established by other SystemState functions.
One final note about this state. The SystemState.Date docs say:
Gets the current date. This Date/Time value changes once per day. Monitor this property to perform actions on a daily basis. Do not use Date to get an accurate clock time; instead, use Time.
The documentation does not explicitly point out the strategy or time of day that SystemState.Date changes, only that it changes daily. I had hoped that it would change at midnight each and every day; however, what we found was that SystemState.Date appeared to change at midnight GMT (or 5PM PST). So, even if you were to schedule a daily routine with this property, it wouldn't necessarily be at the beginning of the day. Given this limitation, we decided to roll our own SystemState.Date event class that fires at midnight each day.
If I get a chance this weekend, I'd like to follow up this post with the problems we've experienced at my lab with the SystemState.CalendarAppointment* properties. They don't appear to change at all no matter what the calendar/appointment structure is like on the phone.
Update February 9th, 3:15AM: My suspicions about the 8-byte array were somewhat correct. It is a representation of DateTime. This is how you properly convert the byte array to a DateTime object:
byte[] rawValue = SystemState.GetValue (SystemProperty.Time) as byte[];
long value = BitConverter.ToInt64 (rawValue, 0);
DateTime time = DateTime.FromFileTime (value);
