Power Notifications and Serial Port

NETCF V2 adds the support of a managed SerialPort class to allow developers to directly access the serial ports on mobile devices, enabling communication with GPS, bar code readers, or other mobile devices within managed code. These peripherals may be sending data to the device on a continuous or intermittent basis. Thus, it is important that the device be listening to the serial port round the clock and be able to respond to the serial input data as they arrive. However, when a Pocket PC is in the idle state for a period of time, the OS automatically puts the device into a suspended state to conserve battery. This causes the serial port objects to become invalid, and they must be closed and re-opened before any data can be sent/received over the port again. NETCF V2 does not provide managed APIs to receive power notifications. However, we can use the RequestPowerNotifications API in Windows CE to obtain notifications whenever the power state of the device changes. This API has been present in Windows CE since 4.0, so it is available to Pocket PC 2003 as well. The following code is an implementation of a managed wrapper to the API.

 using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class PMTest
{
 static void Main() 
 {
       PowerNotifications pn = new PowerNotifications();
       pn.Start();
     Console.WriteLine("starting to sleep");
     Thread.Sleep(20000);
        pn.Stop();
      Console.WriteLine("finally done");
  }
}

public class PowerNotifications
{
  IntPtr ptr = IntPtr.Zero;
   Thread t = null;
    bool done = false;

  [DllImport ("coredll.dll")]
 private static extern IntPtr RequestPowerNotifications(IntPtr hMsgQ, uint Flags);

   [DllImport ("coredll.dll")]
 private static extern uint WaitForSingleObject(IntPtr hHandle, int wait);

   [DllImport ("coredll.dll")]
 private static extern IntPtr CreateMsgQueue(string name, ref MsgQOptions options);

  [DllImport ("coredll.dll")]
 private static extern bool ReadMsgQueue(IntPtr hMsgQ, byte[] lpBuffer, uint cbBufSize, ref uint lpNumRead, int dwTimeout, ref uint pdwFlags);

   public PowerNotifications()
 {
       MsgQOptions options = new MsgQOptions();
        options.dwFlags = 0;
        options.dwMaxMessages = 20;
     options.cbMaxMessage = 10000;
       options.bReadAccess = true;
     options.dwSize = (uint) System.Runtime.InteropServices.Marshal.SizeOf(options);
     ptr = CreateMsgQueue("Test", ref options);
      RequestPowerNotifications(ptr, 0xFFFFFFFF);
     t = new Thread(new ThreadStart(DoWork));
    }

   public void Start()
 {
       t.Start();
  }

   public void Stop()
  {
       done = true;
        t.Abort();
  }

   private void DoWork()
   {
       byte[] buf = new byte[10000];
       uint nRead = 0, flags = 0, res = 0;

     Console.WriteLine("starting loop");
     try
     {
           while(!done)
            {
               res = WaitForSingleObject(ptr, 1000);
               if (res == 0)
               {
                   ReadMsgQueue(ptr, buf, (uint) buf.Length, ref nRead, -1, ref flags);
                    //Console.WriteLine("message: " + ConvertByteArray(buf, 0) + " flag: " + ConvertByteArray(buf, 4));
                 uint flag = ConvertByteArray(buf, 4);
                   string msg = null;
                  switch(flag)
                    {
                       case 65536:
                         msg = "Power On";
                           break;
                      case 131072:
                            msg = "Power Off";
                          break;
                      case 262144:
                            msg = "Power Critical";
                         break;
                      case 524288:
                            msg = "Power Boot";
                         break;
                      case 1048576:
                           msg = "Power Idle";
                         break;
                      case 2097152:
                           msg = "Power Suspend";
                          break;
                      case 8388608:
                           msg = "Power Reset";
                            break;
                      case 0:
                         // non power transition messages are ignored
                            break;
                      default:
                            msg = "Unknown Flag: " + flag;
                          break;
                  }
                   if (msg != null)
                        Console.WriteLine(msg);
             }
           }
       }
       catch(Exception ex)
     {
           if (!done)
          {
               Console.WriteLine("Got exception: " + ex.ToString());
           }
       }
   }

   uint ConvertByteArray(byte[] array, int offset)
 {
       uint res = 0;
       res += array[offset];
       res += array[offset+1] * (uint) 0x100;
      res += array[offset+2] * (uint) 0x10000;
        res += array[offset+3] * (uint) 0x1000000;
      return res;
 }

   [StructLayout(LayoutKind.Sequential)]
       public struct MsgQOptions
   {
       public uint dwSize;
     public uint dwFlags;
        public uint dwMaxMessages;
      public uint cbMaxMessage;
       public bool bReadAccess;
    }
}

You can compile and run the code on a Pocket PC, switch off/on the device and observe the power notification messages on the console (You will need to install the console add-on). Of course, you can modify the power message handling code to close and re-open the serial port instances you have when you receive a "Power On" transition.

Cheers,

Anthony

This posting is provided "AS IS" with no warranties, and confers no rights.