I responded to a thread on Google Groups about the "correct" way to identify the Storage Card path on Windows Mobile devices back in February 2007 (link). According to the Windows Mobile Team Blog, you should use FindFirstFlashCard (link). I've used it successfully on a variety of HTC WM5 SmartPhone devices, however, this issue has recently cropped up as a (potential) issue for one of our MyExperience tool users. The crux of the problem is that you cannot assume \Storage Card\ will be the default path for the storage or flash card on your Windows Mobile device, which is why FindFirstFlashCard is necessary. For example, on the Fujitsu LOOX PocketPC, the storage card path is \SD-MMCard\. The problem seems to be that either some device manufactuers don't properly support the FindFirstFlashCard method or that certain libraries on the device do not use FindFirstFlashCard and improperly assume the storage card is at \Storage Card\. More investigation is needed.
The code I use in MyExperience (actually, the Roam library which MyExperience relies on):
public static String GetFirstFlashCardPath()
{
Win32FindData win32FindData;
IntPtr intPtr = FindFirstFlashCard(out win32FindData);
String path = win32FindData.cFileName;
FindClose(intPtr);
if (String.IsNullOrEmpty(path))
{
throw new ResourceNotFoundException("
Could not find a flash card on this device");
}
return path;
}
The full code is available here.
Showing posts with label P/Invoke. Show all posts
Showing posts with label P/Invoke. Show all posts
Monday, June 30, 2008
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
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
Wednesday, December 20, 2006
Alpha Blending on SmartPhone
I created a quick test app to experiment with the performance of the P/Invoke AlphaBlend calls as discussed in this blog post by Chris Lorton. The AlphaBlend native call does not support per-pixel alpha blending so the alpha value is specified for the entire image. The "incr" label in these two videos is the step value for the alpha channel used in the animation. The frame rates were better than expected, right around 17-20fps.
The tool I am using to record the video is called CoolCapture (I'm using the trial version here). It's very cool. It's essentially a fully featured screen capture tool for Windows Mobile 5 devices. You can generate both videos or pictures. I was fairly impressed with the performance as well; capturing these two videos below only resulted in about a ~6 fps reduction in my animation (thus the blending doesn't look as smooth as it actually is). The trial version limits the recording interval to ~10 seconds (though the webpage says 5 seconds).
Note that the capture resolution was 240x320 (the device resolution); however, this was automatically changed to 640x480 when I uploaded the video to YouTube. This is why the videos have that stretched out look.
The tool I am using to record the video is called CoolCapture (I'm using the trial version here). It's very cool. It's essentially a fully featured screen capture tool for Windows Mobile 5 devices. You can generate both videos or pictures. I was fairly impressed with the performance as well; capturing these two videos below only resulted in about a ~6 fps reduction in my animation (thus the blending doesn't look as smooth as it actually is). The trial version limits the recording interval to ~10 seconds (though the webpage says 5 seconds).
Note that the capture resolution was 240x320 (the device resolution); however, this was automatically changed to 640x480 when I uploaded the video to YouTube. This is why the videos have that stretched out look.
Friday, July 22, 2005
Obtaining Memory Status w/C#
6.5. How do I determine how much memory a device has available?
You can P/Invoke the GetSystemMemoryDivision and GlobalMemorySystem functions to determine how the memory is divided and allocated between program and storage. Definitions of the parameters can be found in the API reference documentation.
from our friendly Smart Client Developer FAQ on msdn.
You can P/Invoke the GetSystemMemoryDivision and GlobalMemorySystem functions to determine how the memory is divided and allocated between program and storage. Definitions of the parameters can be found in the API reference documentation.
from our friendly Smart Client Developer FAQ on msdn.
Sunday, July 10, 2005
Native Interoperability
To do really cool stuff on the SMT5600, sometimes it's necessary to use native code and the P/Invoke functionality. MSDN has some pretty good articles on native interoperability with the .NET Compact Framework.
Here are a select few that interest me:
Here are a select few that interest me:
- Accessing Phone APIS from .NET Compact Framework
- Advanced P/Invoke on the .NET Compact Framework
- An introduction to P/Invoke and marshaling on the .NET Compact Framework
- Creating a P/Invoke Library
For a good introduction into this world, download the sample P/Invoke Library solution and source code for C# and VB. Note however that this code is for Pocket PC 2003, rather than SmartPhone.
Friday, July 08, 2005
Platform Invoke List!
Daniel Moth points to various P/Invoke listings on the web:
Every .NET developer is familiar with platform invocation services (PInvoke), the ability to call native functions declared in dlls via the DllImport attribute. In the early .NET days it was common to see queries about specific API declarations, but 4 years down the road you would have thought people would stop asking and instead use the ready-made ones easily found all over the web. This blog entry is proof of the opposite and it will serve as my pointer next time someone requests a particular Win32 DllImport declaration.
(link)
Every .NET developer is familiar with platform invocation services (PInvoke), the ability to call native functions declared in dlls via the DllImport attribute. In the early .NET days it was common to see queries about specific API declarations, but 4 years down the road you would have thought people would stop asking and instead use the ready-made ones easily found all over the web. This blog entry is proof of the opposite and it will serve as my pointer next time someone requests a particular Win32 DllImport declaration.
(link)
Force Form to Top Level Focus
My .NET Compact Framework application requires that, from time to time, the user gets audibly alerted and a messagebox or form is displayed (using the System.Threading.Timer). Unfortunately, I have been unable to get my Form to display over the home screen. I have tried:
this.Focus();
this.Visible = true;
this.Show();
All in various combinations. I've found a few solutions about this on the web (I haven't tried either yet).
(1)
[DllImport("coredll.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd );
[DllImport("coredll")]
public static extern IntPtr FindWindow(string className, string wndName);
this.Show();
IntPtr hwnd = FindWindow(null, this.Text);
SetForegroundWindow(hwnd );
A full example can be found here.
(2)
If you need to fix your form at the top of the z-order, then rather than continually pulling the window to the front, use SetWindowPos (you'll need to P/Invoke) with the HWND_TOPMOST flag. This will keep your form at the top even if it loses focus. See the code for OpenNETCF.Win32.Win32Window for P/Invoke declaration:-http://vault.netcf.tv/VaultService/VaultWeb/GetFile.aspx?repid=2&path=%24%2fSDF%2fOpenNETCF.Windows.Forms%2fWin32%2fWin32Window.cs&version=2(username guest, password guest)
this.Focus();
this.Visible = true;
this.Show();
All in various combinations. I've found a few solutions about this on the web (I haven't tried either yet).
(1)
[DllImport("coredll.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd );
[DllImport("coredll")]
public static extern IntPtr FindWindow(string className, string wndName);
this.Show();
IntPtr hwnd = FindWindow(null, this.Text);
SetForegroundWindow(hwnd );
A full example can be found here.
(2)
If you need to fix your form at the top of the z-order, then rather than continually pulling the window to the front, use SetWindowPos (you'll need to P/Invoke) with the HWND_TOPMOST flag. This will keep your form at the top even if it loses focus. See the code for OpenNETCF.Win32.Win32Window for P/Invoke declaration:-http://vault.netcf.tv/VaultService/VaultWeb/GetFile.aspx?repid=2&path=%24%2fSDF%2fOpenNETCF.Windows.Forms%2fWin32%2fWin32Window.cs&version=2(username guest, password guest)
Thursday, June 16, 2005
Application Data Path on SmartPhones
You can Platform Invoke the Windows CE API SHGetSpecialFolderLocation with CSIDL_APPDATA (26) as parameter to retrieve the application data path.
RAM is erased when a Smartphone is switched off. This means that application data has to be put in the \Storage folder. The \Storage folder is persisted to flash memory. For more information, see "Storing Data" in the Smartphone Guide found in the Smartphone 2003 SDK.
(from MSDN)
RAM is erased when a Smartphone is switched off. This means that application data has to be put in the \Storage folder. The \Storage folder is persisted to flash memory. For more information, see "Storing Data" in the Smartphone Guide found in the Smartphone 2003 SDK.
(from MSDN)
Subscribe to:
Posts (Atom)
