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






4.64/5 (8投票s)
本文为 Windows Mobile/CE 开发者提供并介绍了一些有用的代码片段。
引言
在本文中,我将介绍许多 Windows Mobile 开发者在为 .NET Compact Framework 平台开发时可能需要的一些技巧。我决定写这篇文章的原因是,这些技巧要么无法通过纯托管代码实现,要么在 .NET CF 提供的庞大类库中很难找到。
我将向您展示如何
- 图形
- 捕获屏幕(创建屏幕截图)
- 重置空闲计时器(保持背光开启)
- 显示/隐藏软输入面板按钮
- 文件系统
- 在“开始”菜单中创建快捷方式
- 获取特殊文件夹的路径(\Program Files、\Windows 等)
- 获取当前执行程序集的文件夹路径
- 获取所有存储卡名称
捕获屏幕
捕获屏幕是在调试图形编程(通常在 GUI 开发中)时的一个有用技巧。要捕获屏幕,我们使用 Charles Petzold 的著作《Programming Windows》中详尽描述的一个著名技巧。
- 获取整个显示的设备上下文
- 将其位块传输到准备好的位图
附带的项目还包含一个示例应用程序,演示了如何使用该代码。
这是代码
[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 开发中的一些典型障碍的源代码。
本文的第二部分可以在 这里 找到。