Windows Mobile programming tricks on .NET Compact Framework 1/2

An article related to usage, quirks and features of Bee Mobile iDesk and Bee Mobile iPack (discontinued).

Published February 21, 2011
Reading time 8 min read

In this article I will describe some tricks which many Windows Mobile developers may need while developing for .NET Compact Framework platform. The reason I decided to write this article is that because the following tricks either cannot be achieved using pure managed code or they are not easy to find out in the big twine of classes .NET CF provides.

I will show you how to:

  • Graphics
    • Capture the screen (create a screen shot)
    • Reset the idle timer (keep the backlight on)
    • Show/hide the soft input panel button
  • File System
    • Create a shortcut in the start menu
    • Get path to special folders (\Program Files, \Windows, etc.)
    • Get path to the folder of the currently executed assembly
    • Get the names of all storage cards

Capture the screen

Capturing the screen can be a useful trick when debugging graphics programming (usually in GUI development). To capture the screen we use a well-known trick thoroughly described in Charles’ Petzold’s book – Programming Windows:

  1. Get Device Context of the entire display
  2. Bit blit it to a prepared bitmap

The attached project also contains a sample application which shows how to use the code:

This is the code:

        [DllImport("coredll.dll")]
        internal static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest,
            int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);

        [DllImport("coredll.dll")]
        private static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("coredll.dll")]
        private static extern IntPtr ReleaseDC(IntPtr HDC);

        const int SRCCOPY = 0x00CC0020;

        public static Bitmap Snapshot(Rectangle rectangle)
        {
            //Use a zeropointer to get hold of the screen context
            IntPtr hdcSrc = GetDC(IntPtr.Zero);

            Bitmap bmpCapture = new Bitmap(rectangle.Width, rectangle.Height);

            //Get graphics from bitmap
            using (Graphics grCapture = Graphics.FromImage(bmpCapture))
            {
                IntPtr hdcDest = grCapture.GetHdc();

                // Blit the image data
                BitBlt(hdcDest, 0, 0,
                    rectangle.Width, rectangle.Height, hdcSrc,
                    rectangle.Left, rectangle.Top, SRCCOPY);

                grCapture.ReleaseHdc(hdcDest);

            }

            ReleaseDC(hdcSrc);

            return bmpCapture;
        }

        public static void Snapshot(string fileName, Rectangle rectangle)
        {
            Snapshot(rectangle).Save(fileName, ImageFormat.Bmp);
        }

The Snapshot method with string parameter can be used to save the bitmap into a file.

Come procede l’andrologo per fare la diagnosi https://organi-erezione.com/levitra-generico/ o seppure per ragioni diverse e finestrella di visione con ingrandimento 3x. Si ritiene che il ginseng aumenti la produzione di ossido nitrico, portando a un migliore flusso sanguigno, finché la frutta non si sarà ridotta in un composto morbido.

Reset the idle timer

Windows Mobile continuously measures how long the user has been idle. When the time specified in the settings runs up, Windows will automatically turn off the backlight. This may not be desirable for some applications. Fortunately, Windows gives us a function to reset this timer whenever we want to. If we want to keep the backlight on, we should reset this idle timer every now-and-then (let’s say every 500 miliseconds). For this a Windows Timer will be handy:

public class ResetIdleTimer
    {

        [DllImport("coredll.dll")]
        private static extern void SystemIdleTimerReset();

        [DllImport("Aygshell.dll")]
        private static extern void SHIdleTimerReset();

        [DllImport("coredll.dll")]
        private static extern int SetSystemPowerState(string pwrState, int pwrStateFlags, int options);

        private const int POWER_STATE_ON = 0x10000;
        private const int POWER_STATE_OFF = 0x20000;
        private const int POWER_STATE_SUSPEND = 0x200000;
        private const int POWER_FORCE = 4096;

        private Timer m_Timer; // timer used to reset the system idle timer

        public ResetIdleTimer(int milisec)
        {
            m_Timer = new Timer();
            m_Timer.Interval = milisec; // in milliseconds
            m_Timer.Tick += new EventHandler(Timer_Tick);
        }

        /// <summary>
        /// Turns on the display and resets the idle timers.
        /// </summary>
        public static void ResetTimer()
        {
            SetSystemPowerState(null, POWER_STATE_ON, POWER_FORCE);
            SystemIdleTimerReset();
            SHIdleTimerReset();
        }

        /// <summary>
        /// Turns the backlight off.
        /// </summary>
        public static void TurnOffBackLight()
        {
            SetSystemPowerState(null, POWER_STATE_OFF, POWER_FORCE);
        }

	    /// <summary>
        /// Call this method to keep the display lit.
        /// </summary>
        public void StartTimer()
        {
            m_Timer.Enabled = true;
        }

	    /// <summary>
        /// Call this method to turn off the functionality provided by this class.
        /// </summary>
        public void StopTimer()
        {
            m_Timer.Enabled = false;
        }

		 private void Timer_Tick(object sender, EventArgs e)
        {
           ResetTimer();
        }
	}

To keep the backlight on, we only need to instantiate the class and then call the StartTimer() method. To stop this functionality simply call StopTimer(). However, if you want to turn off the backlight immediatelly, call TurnOffBackLight() method.

Show or hide the soft input panel button

Although there is a soft input panel component in the standard shipment of Visual Studio components, it only allows us to display or hide the SIP. But if we remove the menu of the form, the SIP button disappears too. Hence the user has no chance to display the SIP if he wanted to. The SIP button is actually a child window of a windows named “MS_SIPBUTTON”. We will therefore first find the “MS_SIPBUTTON” window, get it child window which is a button and then either show or hide it:

        [DllImport("coredll.dll", SetLastError = true)]
        private static extern IntPtr FindWindow(string caption, string className);

        [DllImport("coredll.dll", SetLastError = true)]
        private static extern bool ShowWindow(IntPtr hwnd, int state);

        [DllImport("coredll.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;
        private const int GW_CHILD = 5;

        ///          
        /// Shows the SIP (Software Input Panel) button.         
        ///
        static public void ShowHideSIP(int nShowOrHide)
        {
            IntPtr hSipWindow = FindWindow("MS_SIPBUTTON", "MS_SIPBUTTON");
            if (hSipWindow != IntPtr.Zero)
            {
                IntPtr hSipButton = GetWindow(hSipWindow, GW_CHILD);
                if (hSipButton != IntPtr.Zero)
                {
                    bool res = ShowWindow(hSipButton, nShowOrHide);
                }
            }
        }

And we would use the method so:

    ShowHideSIP(SW_SHOW);
    ShowHideSIP(SW_HIDE);

Create a shortcut in the Start menu

If you create a cab installer for your application you can set it to create a shortcut for your application in the start menu. But there are times when you want to create the shortcut yourself – for example some applications add a shortcut for uninstalling the application or a shortcut to run the settings for it. Well there is an API function exactly for this and this is how it looks like:

        [DllImport("coredll.dll", EntryPoint = "SHCreateShortcut")]
        public static extern void CreateShortcut(string target, string shortcut);

        CreateShortcut(@"\windows\start menu\programs\my email.lnk", @"\windows\tmail.exe");

In this case we created a shortcut in the start menu whose text will be “my email” and if the user taps the shortcut in the start menu thetmail.exe file will be run located in \windows folder.

Get path to special folders

In some language mutations of Windows Mobile / Windows CE the folders such as \Windows, \Windows\Start menu, \Program Files have different names. For example the Start menu sub-folder in Windows folder has this name in Czech language: \Windows\Nabídka Start

If your application is (for whatever reason) accessing this folder and you have hard coded this path, it will likely fail. To find out the names of these special folders you can use this code:

public enum Folders
    {
        Programs = 2,           // \Windows\Start Menu\Programs
        Personal = 5,           // \My Documents
        Startup = 7,            // \Windows\StartUp
        Startmenu = 0x0B,       // \Windows\Start Menu
        Fonts = 0x14,           // \Windows\Fonts
        Favorites = 0x16,       // \Windows\Favorites
        Program_Files = 0x26    // \Program Files
    }

    public class NativeFileSystem
    {
        const int MAX_PATH = 50;
        static bool m_sbExceptionsEnabled;

        [DllImport("coredll.dll", SetLastError = true, EntryPoint = "SHGetSpecialFolderPath")]
        static extern bool SHGetSpecialFolderPath(int hwndOwner, string lpszPath, Folders nFolder, bool fCreate);

        NativeFileSystem()
        { }

        public static string GetFolderPath(Folders folder)
        {
            string sPath = new string(' ', MAX_PATH);
            bool bRet;

            try
            {
                bRet = SHGetSpecialFolderPath(0, sPath, folder, false);
            }
            catch (Exception ex)
            {
                HandleCeError(ex, "GetFolderPath");
                return null;
            }

            if (!bRet)
            {
                int errorNum = Marshal.GetLastWin32Error();
                HandleCeError(new WinCeException("SHGetSpecialFolderPath returned false, likely an invalid constant", errorNum), "GetSpecialFolderPath");
            }

            return sPath;
        }
	}

The HandleCeError method throws a managed exception based on an error which occurred in SHGetSpecialFolderPath function call. It can look like this:

private static void HandleCeError(Exception ex, string method)
        {
            // logging

            if (!ExceptionsEnabled)
            {
                return;
            }

            if (ex is NotSupportedException)
            {
                throw new WinCeException("Bad arguments or incorrect declaration in " + method, 0, ex);
            }

            if (ex is MissingMethodException)
            {
                throw new WinCeException("Entry point not found in " + method, 0, ex);
            }

            if (ex is WinCeException)
            {
                throw ex;
            }

            throw new WinCeException("Miscellaneous exception in " + method, 0, ex);
        }

The sample application shows the list of special folders:

Get the names of all storage cards

Some devices may have several storage cards installed in them. What’s even more important that the storage cards don’t necessarily have to be named \Storage Card. To iterate through all storage cards we don’t even need to use any Platform Invokes. The trick is to find all folders which have ‘Temporary’ attribute set. The code is very simple:

static public List<string> GetStorageCardNames()
        {
            DirectoryInfo rootInfo = new DirectoryInfo("\\");
            List<string> paths = new List<string>();

            foreach (DirectoryInfo dirInfo in rootInfo.GetDirectories())
            {
                if ((dirInfo.Attributes & FileAttributes.Temporary) != 0)
                {
                    //Debug.WriteLine("Directory Name: " + dirInfo.Name)
                    //Debug.WriteLine("     Full Name: " + dirInfo.FullName)
                    paths.Add(dirInfo.FullName);
                }
            }

            return paths;

        }

Conclusion

This concludes the first part of the article. The link to the second part of this article is: http://beemobile4.net/support/technical-articles/windows-mobile-programming-tricks-on-net-compact-framework-22

Free Utils

The whole source code is part of Bee Mobile’s Free Utils project. The goal of Free Utils project is to put together the source code which shows how to overcome some typical hurdles of Windows Mobile / Windows CE development in .NET Compact Framework environment.

[icon url=”http://beemobile4.net/wp-content/uploads/2013/08/download.png”]You can download this utility library free of charge here.[/icon]
[icon url=”http://beemobile4.net/wp-content/uploads/2013/08/download.png”]Sample project with source code from this article can be found here.[/icon]

← Back to technical articles

Need help with implementation?

Bee Mobile helps teams modernize embedded software, migrate legacy applications, and implement robust UI solutions on supported platforms.

Contact Us Explore Products