65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows Mobile .NET Compact Framework 编程技巧:第一部分

2011年2月23日

CPOL

4分钟阅读

viewsIcon

42177

downloadIcon

683

本文为 Windows Mobile/CE 开发者提供并介绍了一些有用的代码片段。

引言

在本文中,我将介绍许多 Windows Mobile 开发者在为 .NET Compact Framework 平台开发时可能需要的一些技巧。我决定写这篇文章的原因是,这些技巧要么无法通过纯托管代码实现,要么在 .NET CF 提供的庞大类库中很难找到。

我将向您展示如何

  • 图形
    • 捕获屏幕(创建屏幕截图)
    • 重置空闲计时器(保持背光开启)
    • 显示/隐藏软输入面板按钮
  • 文件系统
    • 在“开始”菜单中创建快捷方式
    • 获取特殊文件夹的路径(\Program Files\Windows 等)
    • 获取当前执行程序集的文件夹路径
    • 获取所有存储卡名称

捕获屏幕

捕获屏幕是在调试图形编程(通常在 GUI 开发中)时的一个有用技巧。要捕获屏幕,我们使用 Charles Petzold 的著作《Programming Windows》中详尽描述的一个著名技巧。

  1. 获取整个显示的设备上下文
  2. 将其位块传输到准备好的位图

附带的项目还包含一个示例应用程序,演示了如何使用该代码。

Form with an image and button. Form containing the screen shot of itself.

这是代码

[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);
}

带有字符串参数的 Snapshot 方法可用于将位图保存到文件中。

重置空闲计时器

Windows Mobile 会持续测量用户空闲的时间。当设置中指定的时间到期时,Windows 会自动关闭背光。这对于某些应用程序可能不理想。幸运的是,Windows 提供了一个函数,我们可以在任何我们想要的时候重置这个计时器。如果我们想保持背光开启,我们应该时不时地重置这个空闲计时器(比如每 500 毫秒)。为此,Windows 计时器会很有用。

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();
    }
}

要保持背光开启,我们只需要实例化该类,然后调用 StartTimer() 方法。要停止此功能,只需调用 StopTimer()。但是,如果您想立即关闭背光,请调用 TurnOffBackLight() 方法。

显示或隐藏软输入面板按钮

尽管 Visual Studio 组件的标准发行版中有一个软输入面板组件,但它只允许我们显示或隐藏 SIP。但是,如果我们移除了窗体的菜单,SIP 按钮也会消失。因此,用户将无法显示 SIP。SIP 按钮实际上是一个名为 MS_SIPBUTTON 的窗口的子窗口。因此,我们将首先找到 MS_SIPBUTTON 窗口,获取其子窗口(即一个按钮),然后显示或隐藏它。

[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;

/// <summary>
/// Shows the SIP (Software Input Panel) button.
/// <summary>
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);
        }
    }
}

我们将这样使用该方法:

ShowHideSIP(SW_SHOW);
ShowHideSIP(SW_HIDE);

在“开始”菜单中创建快捷方式

如果为应用程序创建 CAB 安装程序,您可以将其设置为在“开始”菜单中为应用程序创建快捷方式。但有时您可能希望自己创建快捷方式——例如,某些应用程序会添加一个卸载应用程序的快捷方式,或者一个运行其设置的快捷方式。那么,有一个 API 函数可以做到这一点,它看起来像这样:

[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");

在这种情况下,我们在“开始”菜单中创建了一个名为“my email”的快捷方式,如果用户点击“开始”菜单中的快捷方式,将运行位于 \windows 文件夹中的 tmail.exe 文件。

获取特殊文件夹的路径

在 Windows Mobile / Windows CE 的某些语言版本中,\Windows\Windows\Start menu\Program Files 等文件夹名称可能不同。例如,Windows 文件夹下的“开始”菜单子文件夹在捷克语中名为 \Windows\Nabídka Start

如果您的应用程序(无论出于何种原因)访问此文件夹并且您硬编码了此路径,它很可能会失败。要找出这些特殊文件夹的名称,您可以使用此代码:

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;
    }
}

HandleCeError 方法会基于 SHGetSpecialFolderPath 函数调用中发生的错误抛出一个托管异常。它可能看起来像这样:

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);
}

示例应用程序显示特殊文件夹列表。

获取所有存储卡名称

某些设备可能安装了多个存储卡。更重要的是,存储卡不一定命名为 \Storage Card。要遍历所有存储卡,我们甚至不需要使用任何平台调用。技巧是找到所有具有 Temporary 属性的文件夹。代码非常简单:

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;
}

结论

本文的第一部分到此结束。所有源代码以及示例应用程序都可以在本文开头或 Bee Mobile 网站上下载:http://beemobile4.net/?mod=products&action=det&id=3。整个源代码是 Bee Mobile 的 Free Utils 项目的一部分。Free Utils 项目的目标是汇集展示如何在 .NET Compact Framework 环境中克服 Windows Mobile / Windows CE 开发中的一些典型障碍的源代码。

本文的第二部分可以在 这里 找到。

© . All rights reserved.