CeRunAppAtTime, CeSetUserNotificationEx, Connection Manager Process Scheduling and how they are affected by Power States in .NET Compact Framework

After releasing the first version of Klaxon, several users reported that Klaxon was not starting at the designated time if the device was sleeping: often several minutes late, or not at all.

My implementation of process scheduling was using Connection Manager. At first I thought the device going to sleep was disabling all the active connections, so the event would not fire. I switched the implementation to use CeRunAppAtTime, and still encountered the same problem. CeRunAppAtTime is deprecated and there were several blog posts describing the various issues people had with it. I then switched the implementation again; this time to use CeSetUserNotificationEx, which is the new hotness in process scheduling. Still the same issue. I did notice something peculiar however: if I scheduled the event with a System Notification Dialog (like a calendar event), it would work.

In all 3 implementations, Klaxon would launch immediately after the device was turned on. Another oddity was that the Klaxon form loaded was almost instantaneously, when generally it has a 5-6 second when I launch it via the Programs list. This led me to believe that it was actually launched and resident, but the device was then somehow put back into suspension. Turns out I was right. There are a few well hidden forum posts which document this issue.

Basically, if the device is not explicitly set to a "power on" state within a few seconds of going into the "resuming" state, it goes back into a suspended state. So the fix is to call SetSystemPowerState, which looks like the following:

using System;
using WindowsMobile.Utilities;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

namespace Klaxon
{
    static class Program
    {
        [DllImport("coredll.dll")]
        extern static UInt32 SetSystemPowerState(String pwsSystemState, UInt32 StateFlags, UInt32 Options);
        const UInt32 POWER_STATE_ON = 0x00010000;
        const UInt32 POWER_FORCE = 0x00001000;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [MTAThread]
        static void Main(string[] args)
        {
            SetSystemPowerState(null, POWER_STATE_ON, POWER_FORCE);
            Run(args);
        }

        static void Run(string[] args)
        {
            InteractiveService.Run(args, typeof(AlarmListForm), new ThreadStart(KlaxonService.Run));
        }
    }
}

Note that I am not calling the InteractiveService.Run in the Main method. This is because when Main is called, all types that it references will have their respective assemblies loaded. In my case, this would be System.Windows.Forms, WindowsMobile.Utilities, and a few others. This would add to the "spin up" time and risk the device being put into suspension before SetSystemPowerState is called.

1 comments:

Anonymous said...

Hi, and sorry for my poor english.

I'm trying to make an Advanced Task Scheduler, but my knowledge of C# is very poor, i'm programming always in VB .NET and i like know how can put WLAN ON/OFF programatically, if is possible in VB .NET, if not in VC#.

my email is: javier@ardanet.net

Thx for advanced

Best Regards