free web page counters

Windows Mobile Pocket PC Smartphone Programming

==>Click here for the SiteMap<==. Original contents with decent amount of source codes.

Thursday, May 11, 2006

Pocket PC Power Management Series 11: Play sound (for example, wav file) when a Pocket PC is suspended

====>SiteMap of this Blog<===

Pocket PC Power Management Series 11: Play sound (for example, wav file) when a Pocket PC is suspended

I am going to write a new series on Pocket PC Power Management topic: How to play sound when Pocket PC is sleeping or suspended. Let me use a simple API to achieve this purpose: PlaySound.

Why care?

If you read my previous series on the Power Management topic, you may ask why we care. If the Pocket PC sleeps, no thread can run, so PlaySound won't even be called. If the PPC is not sleeping, PlaySound is called, so why the hassle? The answer is: Yes, PlaySound will be called, but you may not hear the sound! So the title of this blog is sort of misleading. It should be changed to something like "How to make sure there is a sound when the device is suspended".

Let us say, you write the code to request the PPC to play a wav file, even when it is suspended. A good example is the Alarm program. It might need to play a warning or ringtone at 8PM. This piece of code can be executed at 8PM, guaranteed by Windows CE OS. However, you may be disappointed not hearing any sound. Below I will explain why and how to solve the problem.

Why no Sound?

Ultimately a peripheral device needs to emit the sound. This peripheral must be powered or put into certain state so as to vibrate to produce sound waves. A good chance is: When PlaySound() is called, the Pocket PC is in such a "System Power State" that the sound peripheral is not supposed to work; or in Microsoft terms, the sound peripheral is in such a "Device Power State" mapped from the current "System Power State", that the sound peripheral might have no power, have low power or in standby mode.

For our specific alarm example, the Pocket PC might be in a "System Power State" called "Unattended". The sound peripheral is called "wav1:", which is put into a "D4 Device Power State", mapped from "Unattended" state. "D4" means "No Power", so you won't be able to hear any sound.

To above explanation might be too abstract. If you really need to understand those terms, please read Microsoft Platform Builder documentation on "Power Management".

A Possible Solution

A possible solution to the problem is to change the mapping, so as to change the power state of "wav1:" device. The following code snippet is doing this job:

HRESULT ChangeWav1Mapping()
{
   LRESULT lr = E_FAIL;

   HKEY hKey = NULL;
   DWORD dwSubKeys = 0;
   DWORD dwMaxSubkeyLen = 0;
   TCHAR szSubkeyName[MAX_PATH];

   lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("\\System\\CurrentControlSet\\Control\\Power\\State"), 0, 0, &hKey);
   if (ERROR_SUCCESS != lr) goto FuncExit;

   lr = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeys, &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
   if (ERROR_SUCCESS != lr) goto FuncExit;

   for (int i=0; i<dwSubKeys; i++) {
      DWORD dwSubkeyNameLen = MAX_PATH-1;
      memset(szSubkeyName, 0, MAX_PATH*sizeof(TCHAR));
      lr = RegEnumKeyEx(hKey, i, szSubkeyName, &dwSubkeyNameLen, NULL, NULL, NULL, NULL);
      if (ERROR_SUCCESS != lr) continue;
      
      //ALERT(szSubkeyName);

      HKEY hSubkey = NULL;
      lr = RegOpenKeyEx(hKey, szSubkeyName, 0, 0, &hSubkey);
      if (ERROR_SUCCESS != lr) continue;

      // any wav1: ?
      DWORD dwValueType = 0;
      DWORD dwValueData = 0;
      DWORD dwValueDataLen = sizeof(DWORD);
      lr = RegQueryValueEx(hSubkey, TEXT("wav1:"), NULL, &dwValueType, (LPBYTE)&dwValueData, &dwValueDataLen);
      if (ERROR_SUCCESS == lr && 0 != dwValueData) {
         dwValueData = 0;
         RegSetValueEx(hSubkey, TEXT("wav1:"), NULL, REG_DWORD, (LPBYTE)&dwValueData, sizeof(DWORD));
      }
      RegCloseKey(hSubkey);
   }

   RegFlushKey(hKey);

FuncExit:
   if (hKey) {
      RegCloseKey(hKey);
   }

   return S_OK;
}

Notice this is a piece of bad code. It forces the "wav1:" device to be always powered, no matter what state the Pocket PC is in. Looks like that is the case for most Pocket PC 2003 devices, but probably not true for Pocket PC 5.0 device. So the above code effectively changes the sound behavior of PPC 5.0 device.

The recommended API to use to SetPowerRequirement and ReleasePowerRequirement. I will talk about them in a later post.

Wrap Up

Pocket PC sleeps. Read the following two posts for basis:

There are ways to force the device not to sleep, or request it to run a program at certain time or upon certain event:

The above techniques force the CPU to run, but not necessarily other peripheral devices. In this post we talked about "wav1:" and a possible hacky way to override the power mapping.

====>SiteMap of this Blog<===




Monday, May 08, 2006

XXX.exe is not a valid Pocket PC Application: Troubleshooting guidelines

====>SiteMap of this Blog<===

XXX.exe is not a valid Pocket PC Application: Troubleshooting guidelines

It is not uncommon for a developer to see his/her program cannot run in the emulator or physical device, after struggling with code development, compilation error and build issues. Most typical error is like "XXX.exe is not a valid Pocket PC application", like the following screen cut. Hereby I am trying to write simple guidelines for troubleshooting such issue.

1. Check the Platform

The executable might be built for a platform that does not match the target device. For example, an executable built for x86 emulator cannot run in any physical device, which supports ARM instruction sets.

In WM5.0, Microsoft introduces a build target called "THUMB", which is a subset of ARM instruction sets. An executable built for "THUMB" can only run in WM5.0 device or emulator. You'll get the above error message if running such executable in Pocket PC 2003 or Smartphone 2003 device.

The way to check the platform is to use "dumpbin /header ", and take a look at the line exposing the "machine" information.

The way to fix platform issue is to change project settings in your IDE (EVC4 or Visual Studio 2005). Both IDEs allow you to manually modify the command line option /MACHINE.

2. Check the SubSystem

An executable built for a lower subsystem (for example, 4.20) can run in a WM5.0 device, but not vice versa. If you try to start a program built for 5.0, you will get the warning "XXX is not a valid Pocket PC application".

The way to check the subsystem is to use "dumpbin /header ", and take a look at the line exposing the "subsystem" information.

The way to fix subsystem issue is to change project settings in your IDE (EVC4 or Visual Studio 2005). Both IDEs allow you to manually modify the command line option /SUBSYSTEM.

3. DLL HELL

The previous two issues are easy to identify and fix, but the third one is very concealing and hard to troubleshoot. In general, if a program needs DLLs (who does not?), the DLL must either be present in the system, or already loaded into memory. If a required DLL is not available, the warning is clear and to the point: "Cannot find 'XXX.exe' (or one of its components). Make sure the path and file name are correct and all the required libraries are available."

However, if the DLL is available or already loaded into memory, but the DLL does not export the required functions, CE OS could not verify that linker time dependencies are met. It shows a super-misleading warning "XXX.exe is not a valid Pocket PC application".

It is hard to find which DLL causes the issue, and even harder to solve the issue. On the desktop, you can isolate yourself to certain extent by ensuring that the DLL you built and tested is the one that is loaded by the OS; one way putting the DLL in the same directory as your application. On Windows CE, to save memory, only a single copy of DLL is loaded into physical memory, and multiple processes share the single copy if all requiring such DLL.

To confirm whether the DLL HELL causes the dreaded message, you can try to find what other processes are using the same DLL, then terminate those processes and start your first. If your program works, then definitely your issue is caused by the DLL loaded by other processes. MFC is a good suspect, since there is so many versions of MFC DLLs (although Microsoft has taken extra mile to name the MFC DLLs with version number, debug/retail, ...)

Wrap Up

Machine or SubSystem mismatch is easy to detect and fix. Please read "Trap of Copying Project Settings in Visual Studio 2005 (/Machine: ARM vs. Thumb, /subsystem: windowsce, 4.02 vs 5.01)" for more details.

Issue caused by DLL-mismatch is much harder to detect. If you run into the issue, and you are use MFC, there is a high possibility that multiple versions of MFC DLLs are the culprit.

====>SiteMap of this Blog<===