Visar inlägg med etikett Windows CE. Visa alla inlägg
Visar inlägg med etikett Windows CE. Visa alla inlägg

måndag 26 oktober 2009

Using the PowerState API

It's been a full week since my last post but I've been a bit busy writing sql scripts for an upcoming project at work.. But I did take a couple of minutes to write a simple Windows Mobile Powerstate API sample. Only two function calls are covered but together they hold the last bits you need to be able to write an application that "wakes" up the device at a set time and after performing various tasks are capable to return the device back to the previous state.

I'll show you how right now!

By now you already know how to use the notification library that comes with Windows Mobile. The problem with this API is that although capable of executing an application at a specific time it will not wake the device. In other words; you can initiate a thread but you won't be able to access peripheral devices such as the GPS receiver, open up an internet connection or send text messages. These are all things you often want to do when beeing able to execute applications on set times.
By applying a bit of PowerState magic this small issue may easily be solved.

First of you want to know the current power state of the device. This can easily be found out be calling GetSystemPowerState.

        [DllImport("coredll.dll", SetLastError = false)]
public static extern uint GetSystemPowerState(string sPowerStateName, uint iNameLen, ref uint Flags);

This function will return both the name of the current state as well as all the power state flags that has been set. Power state flags are a simple 32 bit bitmask. The header file declares the different power state flags as constants but I've choosen to declare them as a enum.

    public enum POWER_STATE
{
ON = 0x10000 // on state
, OFF = 0x20000 // no power, full off
, CRITICAL = 0x40000 // critical off
, BOOT = 0x80000 // boot state
, IDLE = 0x100000 // idle state
, SUSPEND = 0x200000 // suspend state
, UNATTENDED = 0x400000 // unattended state
, RESET = 0x800000 // reset state
, USERIDLE = 0x1000000 // user idle state
, BACKLIGHTON = 0x2000000 // device screen backlight on
, PASSWORD = 0x1000000 // this state is password protected
}


Calling GetSystemPowerState before applying a new power state allows you to save the current state and then re-apply it after you've done whatever it is you wanna do. SetSystemPowerState is just as easy to use as its equivelent Get-function.

        [DllImport("coredll.dll", SetLastError = false)]
public static extern uint SetSystemPowerState(string sPowerStatename, POWER_STATE nFlags, uint nForce);



You can pretty much ignore the PowerStateName parameter. nFlags speaks for itself and nForce can be used to force the device into a specific power state.

When using SetSystemPowerState to wake the device you basically call it passing POWER_STATE.ON as the nFlags parameter. And that's it..

I've written a small sample showing you how to use both function calls as well as how the POWER_STATE bitmask works. Enjoy!


            uint iRet;
uint nFlags = 0;
// This would be an excellent time to get aquinted with the StringBuilder object but
// let's do it the cheap way for now.
string sPowerStateName = " ";
iRet = WinCE.Powerstate.GetSystemPowerState(sPowerStateName, (uint)sPowerStateName.Length, ref nFlags);
if (iRet != 0)
{
MessageBox.Show("Failed to retrieve system power state");
return;
}
MessageBox.Show("Powerstate is: " + sPowerStateName);

bool IsOn = Convert.ToBoolean(nFlags & (uint)POWER_STATE.ON);
bool IsOff = Convert.ToBoolean(nFlags & (uint)POWER_STATE.OFF);
bool IsCritical = Convert.ToBoolean(nFlags & (uint)POWER_STATE.CRITICAL);
bool IsBoot = Convert.ToBoolean(nFlags & (uint)POWER_STATE.BOOT);
bool IsIdle = Convert.ToBoolean(nFlags & (uint)POWER_STATE.IDLE);
bool IsSuspended = Convert.ToBoolean(nFlags & (uint)POWER_STATE.SUSPEND);
bool IsUnattended= Convert.ToBoolean(nFlags & (uint)POWER_STATE.UNATTENDED);
bool IsReset = Convert.ToBoolean(nFlags & (uint)POWER_STATE.RESET);
bool IsUserIdle = Convert.ToBoolean(nFlags & (uint)POWER_STATE.USERIDLE);
bool IsBackLightOn = Convert.ToBoolean(nFlags & (uint)POWER_STATE.BACKLIGHTON);
bool IsPassword = Convert.ToBoolean(nFlags & (uint)POWER_STATE.PASSWORD);



            uint iRet;
iRet = WinCE.Powerstate.SetSystemPowerState(null, POWER_STATE.OFF, 1);
if (iRet == 0)
MessageBox.Show("Successfully set new power state");
else
MessageBox.Show("Failed to set new power state");

måndag 19 oktober 2009

Enumerate flash cards on a Windows Mobile based device

Today we'll have a look at how to enumerate available flash cards on a Windows Mobile based device using the native APIs FindFirstFlashCard and FindNextFlashCard. Code is written in C#.

To successfully enumerate the available flash cards, we will need to implement a couple of APIs using DllImport.


        [DllImport("coredll.dll", SetLastError = false)]
public static extern bool FindClose(IntPtr hFindFile);

[DllImport("note_prj.dll", SetLastError = false)]
public static extern IntPtr FindFirstFlashCard(byte[] bFindFlashData);

[DllImport("note_prj.dll", SetLastError = false)]
public static extern bool FindNextFlashCard(IntPtr hFlashCard, byte[] bFindFlashData);



We also need to enum some of the FILE_ATTRIBUTE_xxx values available in Winbase.h.


        public enum FILE_ATTRIBUTE
{
READONLY = 0x1
, HIDDEN = 0x2
, SYSTEM = 0x4
, DIRECTORY = 0x10
, ARCHIVE = 0x20
, INROM = 0x40
, NORMAL = 0x80
, TEMPORARY = 0x100
, SPARSE_FILE = 0x200
, REPARSE_POINT = 0x400
, COMPRESSED = 0x800
, ROMSTATICREF = 0x1000
, ROMMODULE = 0x2000
, ENCRYPTED = 0x4000
}



Next, we need to implement the WIN32_FIND_DATA structure. As you can see in our import of FindFirstFlashCard we don't actually pass an instance of the win32_find_data structure to the API, but instead we pass a byte buffer that are later converted to a win32_find_data structure. I've added a couple of properties to the structure to make working with file attributes a bit more .Net-ish. As you can see, the byte-to-structure-code is located in the constructur the same way as we did with the notification structures last week.


        [StructLayout(LayoutKind.Sequential)]
public class WIN32_FIND_DATA
{
public uint FileAttributes;
private FILETIME ftCreationTime;
private FILETIME ftLastAccessTime;
private FILETIME ftLastWriteTime;
public DateTime dtCreationTime;
public DateTime dtLastAccessTime;
public DateTime dtLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwOID;
public string FileName;
public bool IsReadOnly;
public bool IsHidden;
public bool IsSystem;
public bool IsDirectory;
public bool IsArchive;
public bool IsInRom;
public bool IsNormal;
public bool IsTemporary;
public bool IsSparseFile;
public bool HasReparsePoint;
public bool IsCompressed;
public bool HasRomStaticRef;
public bool IsRomModule;
public bool IsEncrypted;

public WIN32_FIND_DATA()
{
}
public WIN32_FIND_DATA(byte[] Buf, uint Size)
{
int Index = 0;
int iEndOfString = 0;
System.Text.UnicodeEncoding enc = new UnicodeEncoding();
this.FileAttributes = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.ftCreationTime.dwLowDateTime = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.ftCreationTime.dwHighDateTime = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.ftLastAccessTime.dwLowDateTime = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.ftLastAccessTime.dwHighDateTime = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.ftLastWriteTime.dwLowDateTime = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.ftCreationTime.dwHighDateTime = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.nFileSizeHigh = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.nFileSizeLow = BitConverter.ToUInt32(Buf, Index);
Index += 4;
this.dwOID = BitConverter.ToUInt32(Buf, Index);
Index += 4;

iEndOfString = WinCE.findEndOfString(Buf, Index, (int)Size);
this.FileName = enc.GetString(Buf, Index, iEndOfString - Index - 2);
Index = iEndOfString;

this.IsReadOnly = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.READONLY);
this.IsHidden = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.HIDDEN);
this.IsSystem = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.SYSTEM);
this.IsDirectory = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.DIRECTORY);
this.IsArchive = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.ARCHIVE);
this.IsInRom = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.INROM);
this.IsNormal = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.NORMAL);
this.IsTemporary = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.TEMPORARY);
this.IsSparseFile = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.SPARSE_FILE);
this.HasReparsePoint = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.REPARSE_POINT);
this.IsCompressed = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.COMPRESSED);
this.HasRomStaticRef = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.ROMSTATICREF);
this.IsRomModule = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.ROMMODULE);
this.IsEncrypted = Convert.ToBoolean(this.FileAttributes & (uint)FILE_ATTRIBUTE.ENCRYPTED);

this.dtCreationTime = DateTime.FromFileTime(this.ftCreationTime.GetAsLong());
this.dtLastAccessTime = DateTime.FromFileTime(this.ftLastAccessTime.GetAsLong());
this.dtLastWriteTime = DateTime.FromFileTime(this.ftLastWriteTime.GetAsLong());
}
}



Now that's a lot of code!
The win32_find_data structure has a couple of instances of the FILETIME structure. This is a simple structure consisting of just two 32 bit unsigned integer values and a function that returns a 64 bit unsigned integer.


    [StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
public long GetAsLong()
{
long lRet;
lRet = dwHighDateTime;
lRet = lRet << 32;
lRet = dwLowDateTime;
return lRet;
}
}


Returning the filetime structure as a 64 bit integer value makes it possible for us to directly convert it to a .Net DateTime structure using the static DateTime.GetFromFileTime(long lFileTime) function.


this.dtCreationTime = DateTime.FromFileTime(this.ftCreationTime.GetAsLong());


Next up is a simple function that enumerates all flash cards and outputs the path using System.Diagnostics.Trace.WriteLine(string sVal).


            FileSystem.WIN32_FIND_DATA FindData;
Byte[] Buf = new byte[295]; // Struct is 40 bytes + MAX_PATH (255 bytes) = 295 bytes

IntPtr hFlashCard = WinCE.FileSystem.FileAppMgmt.FindFirstFlashCard(Buf);

if (hFlashCard != IntPtr.Zero)
{
FindData = new FileSystem.WIN32_FIND_DATA(Buf, 295);
System.Diagnostics.Trace.WriteLine("Filename:" + FindData.FileName);
Buf = new byte[295];

while (WinCE.FileSystem.FileAppMgmt.FindNextFlashCard(hFlashCard, Buf))
{
FindData = new FileSystem.WIN32_FIND_DATA(Buf, 295);
System.Diagnostics.Trace.WriteLine("Filename:" + FindData.FileName);
}
WinCE.FileSystem.FindClose(hFlashCard);
System.Diagnostics.Trace.WriteLine("Succeeded");
}
else
{
System.Diagnostics.Trace.WriteLine("Failed");
}




And that's about it.. Check back tomorrow when we look closer on a couple other file system related APIs.

måndag 12 oktober 2009

CeSetUserNotificationEx, DateTime.Now and GetLocalTime

It's been known for a while now that DateTime.Now has a few time zone related quirks in it. To overcome these issues I've come to use the native function GetLocalTime as a replacement. GetLocalTime returns SYSTEMTIME and converting it to DateTime is as easy as 1-2-3. Add the following code to the SYSTEMTIME class described earlier.
            public DateTime GetAsDateTime()
  {
      return new DateTime(wYear, wMonth, wDay, wHour, wMinute, wSecond);
  }
Declaration and usage of GetLocalTime is done like this:

        [DllImport("coredll.dll")]
public static extern void GetLocalTime(SYSTEMTIME sysTime);

SYSTEMTIME sysTime = new SYSTEMTIME(DateTime.Now);
GetLocalTime(sysTime);
DateTime dtLocalTime = sysTime.GetAsDateTime();

Now that's that..

What I really wanted to talk about tonight was the rest of the Notification API's. Or some of them at least.
All the API's that we have looked at so far has been marked as obsolete by Microsoft. They are still supported but are so only for back compatibility.
Instead of using CeRunAppAtTime and CeSetUserNotification you are now encouraged to use CeSetUserNotificationEx. The main difference between these two are the use of the CE_NOTIFICATION_TRIGGER struct in the latter. This struct basically holds information on what triggers the notification and what application to run. It's declaration is as follows.

    [StructLayout(LayoutKind.Sequential)]
public class CE_NOTIFICATION_TRIGGER
{
 public UInt32 Size = 0;
 [MarshalAs(UnmanagedType.U4)]
 public CeNotificationType Type;
 [MarshalAs(UnmanagedType.U4)]
 public CeNotificationEvent Event;
 [MarshalAs(UnmanagedType.LPWStr)]
 public string pApplication;
 [MarshalAs(UnmanagedType.LPWStr)]
 public string pArgs;
 public SYSTEMTIME StartTime;
 public SYSTEMTIME EndTime;
 public CE_NOTIFICATION_TRIGGER()
 {
 }
}
The struct is dependent of a couple of ENUMs that specifies the type and event parameter. CeNotificationEvent has already been discussed but CeNotificationType is new. It's values are..

    public enum CeNotificationType
{
  CNT_EVENT = 1           //@flag CNT_EVENT  | System event notification
  ,
  CNT_TIME              //@flag CNT_TIME   | Time-based notification
      ,
  CNT_PERIOD            //@flag CNT_PERIOD | Time-based notification is active for
      // time period between stStart and stEnd
      ,
  CNT_CLASSICTIME         //@flag CNT_CLASSICTIME | equivalent to using (obsolete)
  // CeSetUserNotification function - standard command line is
  // supplied. lpszArguments must be NULL
}

Now what it does is that it tells the API what type of trigger it is. The names speak for themselfs so I won't dig any further in that.

As you can see we use SYSTEMTIME but since the API expects the SYSTEMTIME structs to be a part of the CE_NOTIFICATION_TRIGGER struct then we must change our implementation of SYSTEMTIME from beeing a Class to become a Struct. This means that our older code will break unless you add the "ref" keyword in your API declarations and API calls. As follows..

        [DllImport("CoreDLL.dll", SetLastError = false, EntryPoint="CeRunAppAtTime")]
   public extern static bool RunAppAtTime(string AppName, ref SYSTEMTIME SysTime);

   RunAppAtTime(@"\windows\bubblebreaker.exe", ref sysTime);
As with CeSetUserNotification, CeSetUserNotificationEx alse makes use of the CE_USER_NOTIFICATION struct. Just remember tough that if Type is CNT_EVENT you must pass a NULL-parameter instead of CE_USER_NOTIFICATION! If you don't; CeSetUserNotificationEx will fail!

The actual declaration of CeSetUserNotificationEx are like this:

        [DllImport("CoreDLL.dll", SetLastError = false, EntryPoint="CeSetUserNotificationEx")]
    public static extern uint SetUserNotificationEx([MarshalAs(UnmanagedType.U4)]uint hNotification, CE_NOTIFICATION_TRIGGER Trigger, CE_USER_NOTIFICATION Notification);
The following code will create a time based trigger and connect that trigger to a notification that will fire up \windows\fexplore.exe.
Note that you need to compile this sample with the /unsafe switch. You could always hard code the size of CE_NOTIFICATION_TRIGGER in which case /unsafe won't be necessary. If hard coded; the value is 52.
Also don't forget to pass NULL instead of ceun if you change the trigger type to CNT_EVENT.

            CE_NOTIFICATION_TRIGGER cnt = new CE_NOTIFICATION_TRIGGER();
         CE_USER_NOTIFICATION ceun = new CE_USER_NOTIFICATION();
         cnt.Type =CeNotificationType.CNT_TIME;
         cnt.StartTime = stStart;
         cnt.EndTime = stEnd;
         cnt.pApplication = @"\windows\fexplore.exe";
         cnt.pArgs = "";
         cnt.Size = (UInt32)Marshal.SizeOf(cnt); // Needs to compile with /unsafe

         ceun.ActionFlags = PUN_FLAGS.PUN_LED;
         ceun.sDialogText = "Dialogtext";
         ceun.sDialogTitle = "Title";
         ceun.nMaxSound = 0;

         hNotificationEx = SetUserNotificationEx(0, cnt, ceun);
         if (hNotificationEx > 0)
             MessageBox.Show("Successfully created a notification. Handle: " + hNotificationEx.ToString());
         else
             MessageBox.Show("Failed to create a notification");

Reviewing it, I don't think I forgot something important.. Tomorrow we'll look at how to remove a previously created notification and later this week we'll take a peek at how to do a system wide enumeration of all existing notifications.

torsdag 8 oktober 2009

CeRunAppAtEvent - Using CSharp

Yesterday we looked at how to use CeRunAppAtTime for executing applications at a specific date and time. CeRunAppAtTime has a sibling, CeRunAppAtEvent, that allows for executing applications at specific OS events. The events available to trigger are the following:
    public enum CeNotificationEvent
{
NOTIFICATION_EVENT_NONE
, NOTIFICATION_EVENT_TIME_CHANGE
, NOTIFICATION_EVENT_SYNC_END
, NOTIFICATION_EVENT_ON_AC_POWER
, NOTIFICATION_EVENT_OFF_AC_POWER
, NOTIFICATION_EVENT_NET_CONNECT
, NOTIFICATION_EVENT_NET_DISCONNECT
, NOTIFICATION_EVENT_DEVICE_CHANGE
, NOTIFICATION_EVENT_IR_DISCOVERED
, NOTIFICATION_EVENT_RS232_DETECTED
, NOTIFICATION_EVENT_RESTORE_END
, NOTIFICATION_EVENT_WAKEUP
, NOTIFICATION_EVENT_TZ_CHANGE
, NOTIFICATION_EVENT_MACHINE_NAME_CHANGE
, NOTIFICATION_EVENT_RNDIS_FN_DETECTED
, NOTIFICATION_EVENT_INTERNET_PROXY_CHANGE
, NOTIFICATION_EVENT_LAST = NOTIFICATION_EVENT_INTERNET_PROXY_CHANGE
};

Just as with CeRunAppAtTime, CeRunAppAtEvent resides in CoreDll.dll and its declaration are as follows:
        [DllImport("CoreDLL.dll", SetLastError = false, EntryPoint="CeRunAppAtEvent")]
public extern static bool RunAppAtEvent(string AppName, [MarshalAs(UnmanagedType.U4)]CeNotificationEvent WhichEvent);


To set an application for execution when the system time is changed you call CeRunAppAtEvent like this:
RunAppAtEvent(@"\windows\bubblebreaker.exe", Notify.CeNotificationEvent.NOTIFICATION_EVENT_TIME_CHANGE);

And you cancel the execution of an application like this:
RunAppAtEvent(@"\windows\bubblebreaker.exe", Notify.CeNotificationEvent.NOTIFICATION_EVENT_NONE);

That's it.. Tomorrow we'll look at CeSetUserNotification as well as CeHandleAppNotifications and on Monday I'll explain why you really shouldn't use any of these APIs.

onsdag 7 oktober 2009

CeRunAppAtTime - using CSharp

Some time ago, I needed to run Windows Mobile based application at a specific interval. No sweat - Windows CE offers excellent scheduler API's; CeRunAppAtTime among others.
The catch however was that I had to implement it in CSharp.

CeRunAppAtTime is a simple API that takes two parameters. One string containing that path to the executable and one instance of SYSTEMTIME that specifies the date and time to execute the application.

Here is the CSharp implementation of the SYSTEMTIME struct. Notice that I've implemented it as a class and that the constructor converts a .Net DateTime to the corresponding SYSTEMTIME.

    [StructLayout(LayoutKind.Sequential)]
public class SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;

public SYSTEMTIME(DateTime dtDateTime)
{
this.wYear = (ushort)dtDateTime.Year;
this.wMonth = (ushort)dtDateTime.Month;
this.wDayOfWeek = (ushort)dtDateTime.DayOfWeek;
this.wDay = (ushort)dtDateTime.Day;
this.wHour = (ushort)dtDateTime.Hour;
this.wMinute = (ushort)dtDateTime.Minute;
this.wSecond = (ushort)dtDateTime.Second;
this.wMilliseconds = 0;
}
}


The DllImport declaration of CeRunAppAtTime is as follows:
        [DllImport("CoreDLL.dll", SetLastError = false, EntryPoint="CeRunAppAtTime")]
public extern static bool RunAppAtTime(string AppName, SYSTEMTIME SysTime);


To schedule an application for execution simply do the following:
            DateTime dtStart = DateTime.Now;
dtStart = dtStart.AddMinutes(2);
SYSTEMTIME sysTime = new SYSTEMTIME(dtStart);
if (Notify.Notify.RunAppAtTime(@"\windows\bubblebreaker.exe", sysTime))
MessageBox.Show("Successfully scheduled Bubble Breaker for execution at: " + dtStart.ToShortTimeString());
else
MessageBox.Show("Failed to schedule Bubble Breaker for execution.");

Bonus trick:
To cancel the execution of a scheduled application simply call CeRunAppAtTime with the same application path but pass a null-value instead of a SYSTEMTIME struct.
Ex.
        public static bool CancelRunAppAtTime(string AppName)
{
return RunAppAtTime(AppName, null);
}


Tomorrow we'll look at CeRunAppAtEvent.

Enjoy