Platform detection III: How to detect a touch screen on Windows CE in .NET CF

Pocket PC's have touch screens.  Smartphones don't.  While it is straightforward to determine which of these a Windows Mobile device is, there are Windows CE devices that are neither.  Some may offer touch screens while others don't.  Since a touch screen is really what you may be after (to determine whether you can expect a user to click somewhere on the screen, for example), the better long-term solution would be to check for the touch screen itself.  We can't assume that a future version of Windows Mobile won't change the distinctions that currently exist.  Here I will discuss ways you can do your own detection.

The .NET Compact Framework doesn't offer a way to easily check for the presence of a touch screen.  I worked on adding this feature to NetCF Orcas, but we had to abandon the project after it was very nearly there because we found that some manufacturers did not implement the proper detection APIs, which gave us erroneous results on some hardware.  Instead, I'll show you how we were going to do it, and you should be well on your way.

Strategy

Any Windows CE or Windows Mobile device with a touch screen will have a driver whose filename will be in the registry under the HKLM\Hardware\DeviceMap\Touch key, value "DriverName".  If this value exists in the registry, that is your first clue that a touch screen is present.  Even if the value doesn't exist, the default driver name of touch.dll may still be present and there may be a touch screen.  Then all that's left is check to see if the driver is in \Windows.

And a caveat: A bug in the Windows Mobile Smartphone 2005 emulator leaves a touch screen driver present even though there is no touch screen.  As a result, your detection function should have a special check for Smartphone 2005 and below and return false in that case, regardless of whether a touch screen driver is present.

A C# implementation

This post builds on the first and second posts in the Platform Detection series.  You'll need to build on the code from those posts for this post to compile and run.  Like the previous posts, this code uses partial classes, so you can copy and paste the previous  code into one file and this code into another (removing the prior posts' Main methods) and your project will compile and run.

 using System;
using System.IO;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Text;

namespace PlatformDetection
{
    internal partial class PInvoke
    {
        const int MAX_PATH = 260;
        [DllImport("Coredll.dll")]
        static extern int SHGetSpecialFolderPath(IntPtr hwndOwner, StringBuilder lpszPath, int nFolder, int fCreate);

        public enum SpecialFolders : int
        {
            CSIDL_WINDOWS = 0x0024,
        }
        public static string GetSpecialFolder(SpecialFolders specialFolder)
        {
            StringBuilder path = new StringBuilder(MAX_PATH);
            if (SHGetSpecialFolderPath(IntPtr.Zero, path, (int)specialFolder, 0) == 0)
                throw new Exception("Error getting Windows path.");
            return path.ToString();
        }
    }

    internal partial class PlatformDetection
    {
        public static bool IsTouchScreen()
        {
            string driverFileName = Registry.GetValue(@"HKEY_LOCAL_MACHINE\Hardware\DeviceMap\Touch",
                "DriverName", "touch.dll").ToString();
            string windowsFolder = PInvoke.GetSpecialFolder(PInvoke.SpecialFolders.CSIDL_WINDOWS);
            string driverPath = Path.Combine(windowsFolder, driverFileName);
            bool driverExists = File.Exists(driverPath);

            return
                driverExists &&
                // Windows Mobile 5.0 Smartphone emulator and earlier has a driver, but no touch screen.
                !(IsSmartphone() && IsEmulator() && Environment.OSVersion.Version.Major < 6);
        }
    }

    class TouchscreenProgram
    {
        static void Main(string[] args)
        {
            MessageBox.Show("Touchscreen: " + (PlatformDetection.IsTouchScreen() ? "Yes" : "No"));
        }
    }
}

A C++ non-implementation

I won't actually include the C equivalent of the above code, but be forewarned if you do it yourself: For the C implementation I thought it would be a good idea to actually LoadLibrary("touch.dll") and query the driver to check whether the driver is valid and running.  In my tests, the later call to FreeLibrary would crash my process.  Watching the library reference counter revealed that my LoadLibrary incremented the counter from 1 to 2, and FreeLibrary returned it to 1.  But for some reason some touch screen drivers don't like having FreeLibrary called when they aren't actually being unloaded.  Don't load the library as part of your check, or you may find your app crashing on some devices.

This is the last post in a series of three on platform detection.  This post builds on the previous two: discerning Smartphone and Pocket PC, and detecting the Microsoft Device Emulator.

Comments

  • Anonymous
    March 16, 2007
    是否曾经想过要设计一个.NETCF应用程序,它能够同时在PocketPC和Smartphone平台上使用,并根据不同的平台加载不同的功能模块,那如何检测你的应用程序当前运行在Pock...

  • Anonymous
    June 15, 2007
    I now have three blogs that I post to: this one, JMPInline , and NetCFTeam . Which posts will I put where?

  • Anonymous
    June 21, 2007
    是否曾经想过要设计一个 .NET CF 应用程序,它能够同时在 Pocket PC 和 Smartphone 平台上使用,并根据不同的平台加载不同的功能模块,那如何检测你的应用程序当前运行在 Pocket PC 上还是 Smartphone上,甚至是模拟器上呢?

  • Anonymous
    May 09, 2009
    作者:马宁 在开发Windows CE的应用程序时,经常需要检测平台类型,了解我们的应用程序运行在Pocket PC、Smartphone还是Windows CE上。在这篇文章里,我们介绍如何编写一个应用程序来检测当前运行的平台类型。

  • Anonymous
    May 09, 2009
    作者:马宁在开发WindowsCE的应用程序时,经常需要检测平台类型,了解我们的应用程序运行在PocketPC、Smartphone还是WindowsCE上。在这篇文章里,我们介绍如何编写一个...

  • Anonymous
    May 09, 2009
    作者:马宁 在开发Windows CE的应用程序时,经常需要检测平台类型,了解我们的应用程序运行在Pocket PC、Smartphone还是Windows CE上。在这篇文章里,我们介绍如何编写一个应用程序来检测当前运行的平台类型。