I posted the following to the .NET CF newsgroup this morning:
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).
Showing posts with label .NET CF 2. Show all posts
Showing posts with label .NET CF 2. Show all posts
Friday, October 26, 2007
Tuesday, September 11, 2007
Force Closing the CameraCaptureDialog
The camera API in .NET CF 2 offers a great improvement over .NET CF 1, which had no common interface to the device's camera (you needed to work with the OEM's directly to obtain a reference to their camera driver information). The CameraCaptureDialog class in .NET CF 2 can be used to capture still photographs or video (with or without audio) in a few lines of code. However, like many of the more "advanced" features in the .NET CF 2 library, not all of the OEMs have correctly implemented the managed camera functionality on their devices. For example, one common complaint on discussion boards is not being able to close the camera after invoking it with the CameraCaptureDialog.ShowDialog() method--CameraCaptureDialog.Dispose() does not work. Thus, on some devices, the camera application stays open sucking up memory and disturbing your window z-order even after calling Dispose().
The code below provides a fix to this issue. It relies on a FindWindow P/Invoke to grab the handle to the device's camera application and a DestroyWindow P/Invoke to force it to close. Note that the Cingular 2125 device (which is where I tested this code) always appends [Photo] or [Video] to the camera title (no matter what title you set yourself). Thus, I have a function called GetCameraCaptureDialogTitle that appends that right suffix depending on the capture mode (e.g., video or photo).
//Open the camera capture dialog
CameraCaptureDialog cameraCaptureDialog = new CameraCaptureDialog();
string windowTitle = GetCameraCaptureDialogTitle(cameraCaptureDialog);
DialogResult dr = cameraCaptureDialog.ShowDialog();
if (dr == DialogResult.OK)
{
string capturedFileName = cameraCaptureDialog.FileName;
//do stuff with file!
}
cameraCaptureDialog.Dispose();
//sometimes the camera capture dialog does not close automatically
//we look for the window title and force close it ourselves
IntPtr ptr = WindowUtils.FindWindow(windowTitle);
Debug.WriteLine(string.Format("Found window '{0}' with ptr={1} ", windowTitle, ptr));
if (ptr != IntPtr.Zero)
{
//force the camera closed
WindowUtils.DestroyWindow(ptr);
}
Here are the helper functions:
private string GetCameraCaptureDialogTitle(CameraCaptureDialog dlg)
{
if (dlg.Mode == CameraCaptureMode.Still)
{
return string.Format("{0} [{1}]", dlg.Title, "Photo");
}
else
{
return string.Format("{0} [{1}]", dlg.Title, "Video");
}
}
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr DestroyWindow(IntPtr hWnd);
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static IntPtr FindWindow(string windowTitle)
{
return FindWindow(null, windowTitle);
}
Please e-mail me or post a comment if you have any questions. Also, I use this trick in the MyExperience tool--the source for which can be found here and is open sourced under the BSD license.
Finally, moving beyond CameraCaptureDialog, it would be nice if .NET CF provided events for when a new image or video is captured on the device (e.g., a NewMediaCapturedEvent would be cool to have at the SystemState level if not in the CameraCaptureDialog as well). Furthermore, .NET CF 2 does not allow you to take images/video automatically without user intervention. This feature might be useful for taking timer-based pictures (e.g., for those times when you want to take a self/group portrait but have no one around to take the picture for you). Marcus Perryman has C++ code that turns a Windows Mobile device into a wireless webcam using using DirectShow (see this post). It would be cool to have this fully fleshed out in managed code.
The code below provides a fix to this issue. It relies on a FindWindow P/Invoke to grab the handle to the device's camera application and a DestroyWindow P/Invoke to force it to close. Note that the Cingular 2125 device (which is where I tested this code) always appends [Photo] or [Video] to the camera title (no matter what title you set yourself). Thus, I have a function called GetCameraCaptureDialogTitle that appends that right suffix depending on the capture mode (e.g., video or photo).
//Open the camera capture dialog
CameraCaptureDialog cameraCaptureDialog = new CameraCaptureDialog();
string windowTitle = GetCameraCaptureDialogTitle(cameraCaptureDialog);
DialogResult dr = cameraCaptureDialog.ShowDialog();
if (dr == DialogResult.OK)
{
string capturedFileName = cameraCaptureDialog.FileName;
//do stuff with file!
}
cameraCaptureDialog.Dispose();
//sometimes the camera capture dialog does not close automatically
//we look for the window title and force close it ourselves
IntPtr ptr = WindowUtils.FindWindow(windowTitle);
Debug.WriteLine(string.Format("Found window '{0}' with ptr={1} ", windowTitle, ptr));
if (ptr != IntPtr.Zero)
{
//force the camera closed
WindowUtils.DestroyWindow(ptr);
}
Here are the helper functions:
private string GetCameraCaptureDialogTitle(CameraCaptureDialog dlg)
{
if (dlg.Mode == CameraCaptureMode.Still)
{
return string.Format("{0} [{1}]", dlg.Title, "Photo");
}
else
{
return string.Format("{0} [{1}]", dlg.Title, "Video");
}
}
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr DestroyWindow(IntPtr hWnd);
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static IntPtr FindWindow(string windowTitle)
{
return FindWindow(null, windowTitle);
}
Please e-mail me or post a comment if you have any questions. Also, I use this trick in the MyExperience tool--the source for which can be found here and is open sourced under the BSD license.
Finally, moving beyond CameraCaptureDialog, it would be nice if .NET CF provided events for when a new image or video is captured on the device (e.g., a NewMediaCapturedEvent would be cool to have at the SystemState level if not in the CameraCaptureDialog as well). Furthermore, .NET CF 2 does not allow you to take images/video automatically without user intervention. This feature might be useful for taking timer-based pictures (e.g., for those times when you want to take a self/group portrait but have no one around to take the picture for you). Marcus Perryman has C++ code that turns a Windows Mobile device into a wireless webcam using using DirectShow (see this post). It would be cool to have this fully fleshed out in managed code.
Labels:
.NET CF 2,
Camera,
CameraCaptureDialog,
DestroyWindow,
P/Invoke
Sunday, July 22, 2007
Configuring the GPS Intermediate Driver
I recently purchased the QSTARZ Bluetooth GPS Receiver Model BT-Q818 to play around with some location-aware research ideas I've had. First, the GPS receiver worked out-of-the-box after following the configuration instructions in the directions (or see this post on the Windows Mobile team blog). I tested it both on my Cingular 2125 SmartPhone and Cingular 8125 Pocket PC Phone. Second, it works amazingly well with the new Windows Live Search--I can get smooth map scrolling, hybrid maps, and real time traffic overlays on my phone automatically centered on my current location. To configure Windows Live on WM to use GPS, open Live Search->Menu->Settings and then set the GPS COM port (in my case, COM6). Then, open Map and make sure that Menu->Center on GPS is checked.
I was anxious to start my own hacking. I knew that Windows Mobile 5 shipped with the GPS Intermediate Driver framework, unfortunately I did not realize that this is an unmanaged API. However, both the Windows Mobile 5 SDK and the Windows MObile 6 SDK ship with sample code that provides a managed wrapper around that native GPS API. On my computer, these two samples can be found in:
Well, what if you have a SmartPhone? According to Jason Fuller on the Windows Mobile Team blog, the SmartPhone does not have the GPS Intermediate Driver Control Panel (see his post here). You can, however, manually configure the GPS Driver by modifying the registry. Again, using the Remote Registry Editor found in the Visual Studio Remote Tools directory:
Update 07/24/2007 11:58PM: Some have asked me to post further details on the registry settings I am using on the Pocket PC and SmartPhone. Here's a PDF displaying screenshots of my key/value pairs related to the GPS Intermediate Driver.
I was anxious to start my own hacking. I knew that Windows Mobile 5 shipped with the GPS Intermediate Driver framework, unfortunately I did not realize that this is an unmanaged API. However, both the Windows Mobile 5 SDK and the Windows MObile 6 SDK ship with sample code that provides a managed wrapper around that native GPS API. On my computer, these two samples can be found in:
- C:\Program Files\Windows Mobile 6 SDK\Samples\Smartphone\CS\GPS
- C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Samples\Cs\Gps
- On the desktop, open Start->All Programs->Microsoft Visual Studio 2005->Visual Studio Remote Tools->Remote Registry Editor
- Connect to your Windows Mobile device in the Remote Registry Editor
- Find the path: HKEY_LOCAL_MACHINE\ControlPanel\GPS Settings
- Delete any "Redirect" or "Hide" keys under HKEY_LOCAL_MACHINE\ControlPanel\GPS Settings.
Well, what if you have a SmartPhone? According to Jason Fuller on the Windows Mobile Team blog, the SmartPhone does not have the GPS Intermediate Driver Control Panel (see his post here). You can, however, manually configure the GPS Driver by modifying the registry. Again, using the Remote Registry Editor found in the Visual Studio Remote Tools directory:
- [HKEY_LOCAL_MACHINE\System\CurrentControlSet\GPS Intermediate Driver] "IsEnabled"=dword:00000001
- [HKEY_LOCAL_MACHINE\System\CurrentControlSet\GPS Intermediate Driver\Drivers]
"CurrentDriver"="BT GPS" - [HKEY_LOCAL_MACHINE\System\CurrentControlSet\GPS Intermediate Driver\Drivers\BT GPS]
"CommPort"="COMX:" where X = the hardware COM port (for me COM6)
"FriendlyName"="BT GPS"
"InterfaceType"="COMM"
Update 07/24/2007 11:58PM: Some have asked me to post further details on the registry settings I am using on the Pocket PC and SmartPhone. Here's a PDF displaying screenshots of my key/value pairs related to the GPS Intermediate Driver.
Labels:
.NET CF 2,
Bluetooth,
Configuration,
Context Aware,
GPS,
Location,
WM 5.0
Friday, July 20, 2007
Wallpaper, Screenshots, and Application Switching
To get around some of the Today Screen API limitations (see this post for a Today Screen rant), I've played around quite a bit with programatically switching the wallpaper on a WM device. However, there a number of problems with this approach, most significantly: changing the device wallpaper is slow and once the device has been signaled that the wallpaper has been updated, it does not actually redraw the wallpaper until the desktop is visible (e.g., the desktop is the top level window). For more about this issue, see my .NET CF newsgroup post. Nonetheless, changing the device wallpaper is one of the easiest ways to display ambient information.
A project at Intel Research required that a screenshot be taken of a specified application, cropped, and displayed as the wallpaper on the device. However, as mentioned above, setting the wallpaper on a user's device can be an intrusive operation--not only can it take between 10-30 seconds but the wallpaper update does not actually happen until all applications are minimized making the desktop visible.
I created a little test application which fulfills our requirements and attempts to avoid disrupting the user by waiting to execute until the device is idle. Here's how it works:
Download the source code and the binary. You should be able to compile and deploy as long as you have the Smart Device Framework 2.0 from the OpenNETCF team installed. This sample will probably work with the SDF 2.1 as well but I haven't tested it. Alternatively, you can download the binary zip file and add those .dll references to the source code project. I've also added this sample code to the samples directory in Roam (see SourceForge). Roam is a Windows Mobile open source library which provides functionality like a SQL connection pool, power management functions, and a whole slew of UI code.
Note: this code has been tested on the Cingular 2125 and the T-Mobile SDA device. In addition, I also ran it on the Cingular 8125 Pocket PC Phone and everything worked except for the wallpaper changing code--this is because Roam's wallpaper changer methods only work on the SmartPhone.
A project at Intel Research required that a screenshot be taken of a specified application, cropped, and displayed as the wallpaper on the device. However, as mentioned above, setting the wallpaper on a user's device can be an intrusive operation--not only can it take between 10-30 seconds but the wallpaper update does not actually happen until all applications are minimized making the desktop visible.
I created a little test application which fulfills our requirements and attempts to avoid disrupting the user by waiting to execute until the device is idle. Here's how it works:
- Waits for device idle
- Finds handle to specified application (based on its window title)
- Switches from current app to specified application (saving current foreground window handle)
- Takes screenshot
- Crops screenshot (if necessary)
- Sets background wallpaper to screenshot
- Minimizes all open applications so phone background refreshes
- After background refresh, restores state of open applications
- Returns to saved foreground window handle
- Puts device back to sleep
Download the source code and the binary. You should be able to compile and deploy as long as you have the Smart Device Framework 2.0 from the OpenNETCF team installed. This sample will probably work with the SDF 2.1 as well but I haven't tested it. Alternatively, you can download the binary zip file and add those .dll references to the source code project. I've also added this sample code to the samples directory in Roam (see SourceForge). Roam is a Windows Mobile open source library which provides functionality like a SQL connection pool, power management functions, and a whole slew of UI code.
Note: this code has been tested on the Cingular 2125 and the T-Mobile SDA device. In addition, I also ran it on the Cingular 8125 Pocket PC Phone and everything worked except for the wallpaper changing code--this is because Roam's wallpaper changer methods only work on the SmartPhone.
Saturday, June 02, 2007
Measuring Time on .NET CF 2
If you develop code on both the desktop with the full .NET and on the mobile platform with .NET CF, you have to be careful with the behavioral differences of some classes. DateTime is one such class. On the desktop, DateTime.Now is capable of 10 millisecond resolution (at least on WinXP and Vista). On the mobile platform, however, DateTime.Now only has a resolution of 1 second. Quite the difference if you're using DateTime.Now as an easy way to timestamp data. Note that this also includes the DateTime.Now.Ticks, which on the desktop is measured in 100-nanosecond units but is only at the 1-second level in Windows Mobile. So, what are the alternatives?
The most straightforward method may be Environment.TickCount (which is different from DateTime.Ticks). Environment.TickCount represents a 32-bit signed integer containing the amount of time in milliseconds that has pass since the last time the computer was started. The problem with TickCount, however, is that it is only a 32 bit value. Therefore, if the system runs continuously (e.g., no restarting) for 24.9 days the TickCount value will reach int.MaxValue and then wrap to int.MinValue, which is a negative value. Then, for the next 24.9 days, Environment.TickCount will increment from int.MinValue to 0 and start the cycle all over again.
Alternatively, you can P/Invoke QueryPerformanceCounter and QueryPerformanceFrequency. The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter, if one exists, on the computer. The QueryPerformanceFrequency function retrieves the frequency of the high-performance counter, if it exists. The frequency cannot change while the system is running. The frequency is also platform dependent. I'm not sure if any mobile device ships with a performance counter that offers higher resolution than Environment.TickCount but it is worth experimenting with. Here's what MSDN has to say about "high resolution timers"
If a high-resolution performance counter exists on the system, you can use the QueryPerformanceFrequency function to express the frequency, in counts per second. The value of the count is processor dependent. On some processors, for example, the count might be the cycle rate of the processor clock.
The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter. By calling this function at the beginning and end of a section of code, an application essentially uses the counter as a high-resolution timer. For example, suppose that QueryPerformanceFrequency indicates that the frequency of the high-resolution performance counter is 50,000 counts per second. If the application calls QueryPerformanceCounter immediately before and immediately after the section of code to be timed, the counter values might be 1500 counts and 3500 counts, respectively. These values would indicate that .04 seconds (2000 counts) elapsed while the code executed
Here's the code to use the QueryPerformanceCounter in .NET CF 2 on Windows Mobile.
public static class PerformanceUtils
{
[DllImport("coredll.dll", EntryPoint = "QueryPerformanceCounter")]
private static extern bool QueryPerformanceCounter(out long count);
[DllImport("coredll.dll", EntryPoint = "QueryPerformanceFrequency")]
private static extern bool QueryPerformanceFrequency(out long countsPerSecond);
//these two variables are initialized in the PerformanceUtils static constructor
public static readonly long Frequency;
public static readonly long FrequencyInMs;
static PerformanceUtils()
{
if (QueryPerformanceFrequency(out Frequency) == false)
{
throw new Exception("The high resolution timer is not available on this device.");
}
FrequencyInMs = Frequency / 1000;
}
public static long GetTimestampMs()
{
long count;
QueryPerformanceCounter(out count);
return (long)Math.Round(count / (double)FrequencyInMs);
}
public static long GetPerformanceCount()
{
long count;
QueryPerformanceCounter(out count);
return count;
}
}
Update 06/07/2007 @ 1:45PM: Note that on a PocketPC the Environment.TickCount value is reset when you "soft or hard reset" the device. It is not reset when you suspend and resume (power off/on) the device. This is according to Ercan Turkarslan from Microsoft Mobile Devices Developer Support. On a Pocket PC Phone or a SmartPhone, the Environment.TickCount value is reset when you power off/on the device.
The most straightforward method may be Environment.TickCount (which is different from DateTime.Ticks). Environment.TickCount represents a 32-bit signed integer containing the amount of time in milliseconds that has pass since the last time the computer was started. The problem with TickCount, however, is that it is only a 32 bit value. Therefore, if the system runs continuously (e.g., no restarting) for 24.9 days the TickCount value will reach int.MaxValue and then wrap to int.MinValue, which is a negative value. Then, for the next 24.9 days, Environment.TickCount will increment from int.MinValue to 0 and start the cycle all over again.
Alternatively, you can P/Invoke QueryPerformanceCounter and QueryPerformanceFrequency. The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter, if one exists, on the computer. The QueryPerformanceFrequency function retrieves the frequency of the high-performance counter, if it exists. The frequency cannot change while the system is running. The frequency is also platform dependent. I'm not sure if any mobile device ships with a performance counter that offers higher resolution than Environment.TickCount but it is worth experimenting with. Here's what MSDN has to say about "high resolution timers"
If a high-resolution performance counter exists on the system, you can use the QueryPerformanceFrequency function to express the frequency, in counts per second. The value of the count is processor dependent. On some processors, for example, the count might be the cycle rate of the processor clock.
The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter. By calling this function at the beginning and end of a section of code, an application essentially uses the counter as a high-resolution timer. For example, suppose that QueryPerformanceFrequency indicates that the frequency of the high-resolution performance counter is 50,000 counts per second. If the application calls QueryPerformanceCounter immediately before and immediately after the section of code to be timed, the counter values might be 1500 counts and 3500 counts, respectively. These values would indicate that .04 seconds (2000 counts) elapsed while the code executed
Here's the code to use the QueryPerformanceCounter in .NET CF 2 on Windows Mobile.
public static class PerformanceUtils
{
[DllImport("coredll.dll", EntryPoint = "QueryPerformanceCounter")]
private static extern bool QueryPerformanceCounter(out long count);
[DllImport("coredll.dll", EntryPoint = "QueryPerformanceFrequency")]
private static extern bool QueryPerformanceFrequency(out long countsPerSecond);
//these two variables are initialized in the PerformanceUtils static constructor
public static readonly long Frequency;
public static readonly long FrequencyInMs;
static PerformanceUtils()
{
if (QueryPerformanceFrequency(out Frequency) == false)
{
throw new Exception("The high resolution timer is not available on this device.");
}
FrequencyInMs = Frequency / 1000;
}
public static long GetTimestampMs()
{
long count;
QueryPerformanceCounter(out count);
return (long)Math.Round(count / (double)FrequencyInMs);
}
public static long GetPerformanceCount()
{
long count;
QueryPerformanceCounter(out count);
return count;
}
}
Update 06/07/2007 @ 1:45PM: Note that on a PocketPC the Environment.TickCount value is reset when you "soft or hard reset" the device. It is not reset when you suspend and resume (power off/on) the device. This is according to Ercan Turkarslan from Microsoft Mobile Devices Developer Support. On a Pocket PC Phone or a SmartPhone, the Environment.TickCount value is reset when you power off/on the device.
Labels:
.NET CF 2,
P/Invoke,
Performance,
Performance Monitoring,
Timer
Monday, January 23, 2006
Subscribe to:
Posts (Atom)


