Windows Mobile日益成熟,开发者队伍也越来越壮大。作为一个10年的计算机热爱者和程序员,我也经受不住新技术的诱惑,倒腾起Mobile这个玩具。Mobile和Windows的血缘关系决定了它在Windows程序员中的受欢迎程度,在网络上随便搜索一下,关于Mobile应用、开发的文章数不胜数。可是对于计划开发一款全能的Desktop<=>Device同步管理程序的我来说,却发现资源少得可怜——仅仅在MSDN和两个国外的Developer网站上发现了一点资料。现在我仍然在搜索学习中,在这里把我迄今掌握的一点心得写出来,希望能起到抛砖引玉的功效。另请各位高手指正。
Mobile的开发资源很繁杂,很多人常常弄不清究竟要安装哪些工具才能搭建出合适的开发环境。但是我相信Microsoft SMARTPHONE 2003 SDK和Microsoft POCKETPC 2003 SDK是所有的人都知道的,它们分别为SmartPhone和PocketPC提供了必不可少的支持。兄弟我至今没有做出什么成绩,囊中羞涩,好容易攒了台SmartPhone,今天就已Microsoft SMARTPHONE 2003 SDK为例吧。
SMARTPHONE SDK包含了大量的API,列表如下(选自SDK文档,本人翻译):
| Smartphone API |
Description |
| ActiveSync |
创建移动应用程序安装和配置,同步服务模块,过滤器和协助访问ActiveSync服务的应用。 |
| Bluetooth API |
创建支持蓝牙设备的Mobile应用程序,比如耳机,打印机和其他移动设备。 |
| CE Messaging (CEMAPI) |
创建messaging applications |
| Configuration Service Providers |
创建可配置各种CSPs(Configuration Service Providers)的应用 |
| Connection Manager |
创建可自动管理移动设备网络连接的应用 |
| Control API |
在你的移动应用程序中使用Smartphone控件 |
| Device Management API |
创建可远程访问移动设备配置管理的应用程序 |
| Game API (GAPI) |
创建高性能的实时游戏 |
| Home Screen API |
创建用户界面插件 |
| HTML Control |
创建可显示HTML文本和嵌入图片,解析XML和绑定URL到别名的应用程序 |
| MIDI |
创建可播放MIDI文件的应用程序 |
| Object Exchange (OBEX) |
创建对象交换应用,允许移动设备自由的通过无线交换数据 |
| Pocket Outlook Object Model (POOM) API |
创建可操作收件箱部件(联系人,日历和任务)的移动应用程序 |
| Projects Control |
创建可以和Projects Control交互的应用 |
| Remote API (RAPI) |
创建可以同步或控制移动设备的桌面应用程序 |
| Speech Recognizer |
为应用程序增加语音识别功能(比如语音拨号) |
| Telephony |
创建支持电话和短信的应用程序 |
| User Interface |
管理输入面板,增加用户界面元素到你的移动应用程序 |
| Vibrate API |
为你的移动应用程序增加震动特性 |
| Voice Recorder Control |
创建移动数字录音程序 |
| Windows User Interface Controls |
创建将移动扩展合并到标准Microsoft® Windows® CE用户界面控件的应用 |
要创建Desktop<=>Device的桌面同步管理程序,主要就依靠SDK API中的Remote API(RAPI)。RAPI 库由一组函数组成,这些函数可用于通过桌面应用程序管理设备,包括设备的目录文件、设备的注册表和系统信息。废话不多说,我们先来看看如何管理设备中的目录文件。
RAPI提供了一组文件管理的方法(不完全列表,详见SDK文档。选自SDK文档,本人翻译):
| Function |
Description |
| CeCopyFile |
复制文件 |
| CeCreateDirectory |
创建目录 |
| CeCreateFile |
创建,打开文件、管道、通讯资源、磁盘设备或者控制台。返回一个句柄用来访问对象。 |
| CeDeleteFile |
删除文件 |
| CeFindAllFiles |
从指定的Windows CE目录中获取所有文件和目录的信息,并且复制到一个包含CE_FIND_DATA结构的数组中 |
| CeFindFirstFile |
在目录中查找匹配给定文件名的一个文件 |
| CeFindClose |
关闭指定的查找句柄,CeFindFirstFile和CeFindNextFile 函数用这个句柄查找文件 |
| CeFindNextFile |
从上一次访问的CeFindFirstFile继续查找文件 |
| CeGetFileAttributes |
返回指定文件或目录的属性 |
| CeGetFileSize |
获取指定文件的字节大小 |
| CeGetFileTime |
获取文件创建日期时间,最后访问日期时间和最后修改日期时间 |
| CeMoveFile |
移动(重命名)一个文件或者目录 |
| CeReadFile |
从文件指针处读取文件数据 |
| CeWriteFile |
从文件指针处写入文件数据 |
首先要说明的是,任何RAPI操作都需要首先初始化与设备的连接:
| Function |
Description |
| CeRapiInit (RAPI) |
创建Windows CE remote application-programming interface (RAPI). |
[C#.NET]
using System; using System.Runtime.InteropServices;
public class RAPI { public void RapiInit() { int ret = CeRapiInit();
if( ret != 0) { // 连接失败,获取失败代码 int e = CeRapiGetError();
// 抛出异常 Marshal.ThrowExceptionForHR(ret); }
// 连接成功 // To Do
}
[DllImport("rapi.dll", CharSet=CharSet.Unicode)] internal static extern int CeRapiGetError();
[DllImport("rapi.dll", CharSet=CharSet.Unicode)] internal static extern int CeRapiInit(); }
|
| |
连接建立后,就可以进行文件操作了。看一个将文件复制到设备的例子:
[C#.NET]
using System; using System.Runtime.InteropServices; using System.IO;
public class RAPI { private const uint GENERIC_WRITE = 0x40000000; // 设置读写权限 private const short CREATE_NEW = 1; // 创建新文件 private const short FILE_ATTRIBUTE_NORMAL = 0x80; // 设置文件属性 private const short INVALID_HANDLE_VALUE = -1; // 错误句柄
IntPtr remoteFile = IntPtr.Zero; String LocalFileName = @"c:\test.txt"; // 本地计算机文件名 String RemoteFileName = @"\My Documents\test.txt"; // 远程设备文件名 byte[] buffer = new byte[0x1000]; // 传输缓冲区定义为4k FileStream localFile;
int bytesread = 0; int byteswritten = 0; int filepos = 0;
public RapiFile() { // 创建远程文件 remoteFile = CeCreateFile(RemoteFileName, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
// 检查文件是否创建成功 if ((int)remoteFile == INVALID_HANDLE_VALUE) { throw new Exception("Could not create remote file"); }
// 打开本地文件 localFile = new FileStream(LocalFileName, FileMode.Open);
// 读取4K字节 bytesread = localFile.Read(buffer, filepos, buffer.Length); while(bytesread > 0) { // 移动文件指针到已读取的位置 filepos += bytesread;
// 写缓冲区数据到远程设备文件 if(! Convert.ToBoolean(CeWriteFile(remoteFile, buffer, bytesread, ref byteswritten, 0))) { // 检查是否成功,不成功关闭文件句柄,抛出异常 CeCloseHandle(remoteFile); throw new Exception("Could not write to remote file"); } try { // 重新填充本地缓冲区 bytesread = localFile.Read(buffer, 0, buffer.Length); } catch(Exception) { bytesread = 0; } }
// 关闭本地文件 localFile.Close();
// 关闭远程文件 CeCloseHandle(remoteFile); }
// 声明要引用的API [DllImport("rapi.dll", CharSet=CharSet.Unicode)] internal static extern int CeCloseHandle(IntPtr hObject);
[DllImport("rapi.dll", CharSet=CharSet.Unicode)] internal static extern int CeWriteFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfbytesToWrite, ref int lpNumberOfbytesWritten, int lpOverlapped);
[DllImport("rapi.dll", CharSet=CharSet.Unicode, SetLastError=true)] internal static extern IntPtr CeCreateFile( string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile); }
|
| |
操作完毕后在合适的时候需要断开RAPI连接,使用如下函数(选自SDK文档,本人翻译):
| Function |
Description |
| CeRapiUninit (RAPI) |
销毁Windows CE remote application-programming interface (RAPI). |
[C#.NET]
using System; using System.Runtime.InteropServices;
public class RAPIUninit { public RAPIUninit() { CeRapiUninit(); }
// 声明要引用的API [DllImport("rapi.dll", CharSet=CharSet.Unicode)] internal static extern int CeRapiUninit(); }
|
| |
文件操作的函数有很多,基本思路都是一样的,在这里就不一一举例了。请注意文件句柄使用以后一定要释放。
我们再看一个取系统信息的例子,RAPI提供了一些取系统信息的函数(选自SDK文档,本人翻译):
| Function |
Description |
| CeGetSystemInfo |
返回当前系统信息 |
| CeGetSystemMetrics |
获取Windows元素的尺寸和系统设置 |
| CeGetVersionEx |
获取当前运行的操作系统版本的扩展信息 |
| CeGetSystemPowerStatusEx |
获取电池状态 |
| CeGlobalMemoryStatus |
获取系统物理内存和虚拟内存信息 |
| CeGetStoreInformation |
获取存储器信息并填入STORE_INFORMATION结构 |
[C#.net]
public class RAPI
{
SYSTEM_INFO si; // 系统信息
OSVERSIONINFO versionInfo; // 版本信息
SYSTEM_POWER_STATUS_EX PowerStatus; // 电源信息
MEMORYSTATUS ms; // 内存信息
String info;
public void systemInfo()
{
// 检索系统信息
try
{
CeGetSystemInfo(out si);
}
catch(Exception)
{
throw new Exception("Error retrieving system info.");
}
// 检索设备操作系统版本号。
bool b;
versionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFO)); // 设置为结构大小
b = CeGetVersionEx(out versionInfo);
if(!b)
{
throw new Exception("Error retrieving version information.");
}
// 检索设备电源状态
try
{
CeGetSystemPowerStatusEx(out PowerStatus, true); // true 表示读取最新的电源信息,否则将从缓存中获得
}
catch(Exception)
{
throw new Exception("Error retrieving system power status.");
}
// 检索设备内存状态
CeGlobalMemoryStatus( out ms );
// 设置检索信息的格式。
info = "The connected device has an ";
switch (si.wProcessorArchitecture)
{
case ProcessorArchitecture.Intel:
info += "Intel processor.\n";
break;
case ProcessorArchitecture.MIPS:
info += "MIPS processor.\n";
break;
case ProcessorArchitecture.ARM:
info += "ARM processor.\n";
break;
default:
info = "unknown processor type.\n";
break;
}
info += "OS version: " + versionInfo.dwMajorVersion + "." + versionInfo.dwMinorVersion + "." +
versionInfo.dwBuildNumber + "\n";
if (PowerStatus.ACLineStatus == 1)
{
info += "On AC power:YES\n";
}
else
{
info += "On AC power:NO \n";
}
info += "Battery level: " + PowerStatus.BatteryLifePercent + "%\n";
info += "Total memory: " + String.Format("{0:###,###,###}", ms.dwTotalPhys) +
"\n";
// 显示结果。
Console.WriteLine(info);
}
#region 声明API,详见SDK文档
[DllImport("rapi.dll", CharSet=CharSet.Unicode, SetLastError=true)]
internal static extern int CeGetSystemInfo(out SYSTEM_INFO pSI);
[DllImport("rapi.dll", CharSet=CharSet.Unicode, SetLastError=true)]
internal static extern bool CeGetVersionEx(out OSVERSIONINFO lpVersionInformation);
[DllImport("rapi.dll", CharSet=CharSet.Unicode, SetLastError=true)]
internal static extern bool CeGetSystemPowerStatusEx(out SYSTEM_POWER_STATUS_EX pStatus, bool fUpdate);
[DllImport("rapi.dll", CharSet=CharSet.Unicode, SetLastError=true)]
internal static extern void CeGlobalMemoryStatus(out MEMORYSTATUS msce);
#endregion
#region 声明结构
///
/// 处理器架构 (CeGetSystemInfo)
///
public enum ProcessorArchitecture : short
{
///
/// Intel
///
Intel = 0,
///
/// MIPS
///
MIPS = 1,
///
/// Alpha
///
Alpha = 2,
///
/// PowerPC
///
PPC = 3,
///
/// Hitachi SHx
///
SHX = 4,
///
/// ARM
///
ARM = 5,
///
/// IA64
///
IA64 = 6,
///
/// Alpha 64
///
Alpha64 = 7,
///
/// Unknown
///
Unknown = -1
}
///
/// 移动设备内存信息
///
[StructLayout(LayoutKind.Sequential)]
public struct MEMORYSTATUS
{
internal uint dwLength;
///
/// 当前内存占用 (%)
///
public int dwMemoryLoad;
///
/// 物理内存总量
///
public int dwTotalPhys;
///
/// 可用物理内存
///
public int dwAvailPhys;
///
/// 分页数
///
public int dwTotalPageFile;
///
/// 未分页
///
public int dwAvailPageFile;
///
/// 虚拟内存总量
///
public int dwTotalVirtual;
///
/// 可用虚拟内存
///
public int dwAvailVirtual;
}
///
/// 移动设备电源信息
///
public struct SYSTEM_POWER_STATUS_EX
{
///
/// 交流电状态
///
public byte ACLineStatus;
///
/// 电池充电状态。1 High,2 Low,4 Critical,8 Charging,128 No system battery,255 Unknown status
///
public byte BatteryFlag;
///
/// 电池电量剩余百分比
///
public byte BatteryLifePercent;
///
/// 保留字段,设置为0
///
internal byte Reserved1;
///
/// 电池电量剩余时间(秒)
///
public int BatteryLifeTime;
///
/// 电池充满电的总可用时间(秒)
///
public int BatteryFullLifeTime;
///
/// 保留字段,设置为0
///
internal byte Reserved2;
///
/// 后备电池状态
///
public byte BackupBatteryFlag;
///
/// 后备电池剩余电量百分比
///
public byte BackupBatteryLifePercent;
///
/// 保留字段,设置为0
///
internal byte Reserved3;
///
/// 后备电池电量剩余时间(秒)
///
public int BackupBatteryLifeTime;
///
/// 后备电池充满电的总可用时间(秒)
///
public int BackupBatteryFullLifeTime;
}
///
/// OSVERSIONINFO platform type
///
public enum PlatformType : int
{
///
/// Win32 on Windows CE.
///
VER_PLATFORM_WIN32_CE = 3
}
///
/// 操作系统版本信息
///
public struct OSVERSIONINFO
{
internal int dwOSVersionInfoSize;
///
/// 主版本信息
///
public int dwMajorVersion;
///
/// 副版本信息
///
public int dwMinorVersion;
///
/// 编译信息
///
public int dwBuildNumber;
///
/// 操作系统类型
///
public PlatformType dwPlatformId;
}
///
/// 处理器类型 (CeGetSystemInfo)
///
public enum ProcessorType : int
{
///
/// 386
///
PROCESSOR_INTEL_386 = 386,
///
/// 486
///
PROCESSOR_INTEL_486 = 486,
///
/// Pentium
///
PROCESSOR_INTEL_PENTIUM = 586,
///
/// P2
///
PROCESSOR_INTEL_PENTIUMII = 686,
///
/// IA 64
///
PROCESSOR_INTEL_IA64 = 2200,
///
/// MIPS 4000 series
///
PROCESSOR_MIPS_R4000 = 4000,
///
/// Alpha 21064
///
PROCESSOR_ALPHA_21064 = 21064,
///
/// PowerPC 403
///
PROCESSOR_PPC_403 = 403,
///
/// PowerPC 601
///
PROCESSOR_PPC_601 = 601,
///
/// PowerPC 603
///
PROCESSOR_PPC_603 = 603,
///
/// PowerPC 604
///
PROCESSOR_PPC_604 = 604,
///
/// PowerPC 620
///
PROCESSOR_PPC_620 = 620,
///
/// Hitachi SH3
///
PROCESSOR_HITACHI_SH3 = 10003,
///
/// Hitachi SH3E
///
PROCESSOR_HITACHI_SH3E = 10004,
///
/// Hitachi SH4
///
PROCESSOR_HITACHI_SH4 = 10005,
///
/// Motorola 821
///
PROCESSOR_MOTOROLA_821 = 821,
///
/// Hitachi SH3
///
PROCESSOR_SHx_SH3 = 103,
///
/// Hitachi SH4
///
PROCESSOR_SHx_SH4 = 104,
///
/// Intel StrongARM
///
PROCESSOR_STRONGARM = 2577,
///
/// ARM720
///
PROCESSOR_ARM720 = 1824,
///
/// ARM820
///
PROCESSOR_ARM820 = 2080,
///
/// ARM920
///
PROCESSOR_ARM920 = 2336,
///
/// ARM 7
///
PROCESSOR_ARM_7TDMI = 70001
}
///
/// CeGetSystemInfo的数据结构
///
public struct SYSTEM_INFO
{
///
/// 处理器架构
///
public ProcessorArchitecture wProcessorArchitecture;
///
/// 保留
///
internal ushort wReserved;
///
/// Specifies the page size and the granularity of page protection and commitment.
///
public int dwPageSize;
///
/// 应用程序可访问内存地址的最小值
///(Pointer to the lowest memory address accessible to applications
/// and dynamic-link libraries (DLLs). )
///
public int lpMinimumApplicationAddress;
///
/// 应用程序可访问内存地址的最大值(Pointer to the highest memory address
/// accessible to applications and DLLs.)
///
public int lpMaximumApplicationAddress;
///
/// Specifies a mask representing the set of processors configured into
/// the system. Bit 0 is processor 0; bit 31 is processor 31.
///
public int dwActiveProcessorMask;
///
/// 处理器数量(Specifies the number of processors in the system.)
///
public int dwNumberOfProcessors;
///
/// 处理器类型(Specifies the type of processor in the system.)
///
public ProcessorType dwProcessorType;
///
/// Specifies the granularity with which virtual memory is allocated.
///
public int dwAllocationGranularity;
///
/// Specifies the system architecture-dependent processor level.
///
public short wProcessorLevel;
///
/// Specifies an architecture-dependent processor revision.
///
public short wProcessorRevision;
}
#endregion
}
RAPI可以做的事情还有很多,比如取注册表信息,提供对 Microsoft ActiveSync 底层功能的访问,运行远程应用程序,文件列表等等。只要仔细阅读SDK文档,相信都不是难事。
作为Mobile设备的桌面管理程序,备份通话记录,联机发送短信等功能是必不可少的。在我刚发现RAPI的时候,以为和前面的例子一样,有现成的函数可以使用。仔细研究以后才发现要复杂的多。相信这是很多朋友的希望实现的功能,所以班门弄斧,简述如下。
RAPI并没有提供通话,SIM卡和短信方面的函数,它们分别包含在SmartPhone SDK的Phone API,SIM Manager和Short Message Service中。然而包含这些API的phone.dll,cellcore.dll和sms.dll都是储存在设备上的,在Windows上运行的程序是无法调用存储在远程设备上的动态连接库的。
我们仍然需要RAPI。虽然它没有提供直接访问通话记录和短信方面的操作,但是它提供了一个特殊的函数:
| Function |
Description |
| CeRapiInvoke |
使用一种通用的机制执行远程程序 |
CeRapiInvoke的原型如下:
STDAPI_( HRESULT ) CeRapiInvoke(
LPCWSTR pDllPath, // 包含API的Dll文件完整路径
LPCWSTR pFunctionName, // 要调用的函数名
DWORD cbInput, // 函数输入缓冲区大小
BYTE * pInput, // 函数输入缓冲区指针
DWORD * pcbOutput, // 函数输出缓冲区大小
BYTE ** ppOutput, // 函数输出缓冲区指针
IRAPIStream ** ppIRAPIStream, // 指定使用阻塞模式或流模式
DWORD dwReserved); // 保留
CeRapiInvoke将允许我们调用远程设备中的任何API函数!不过不是直接调用,仍然需要对远程API进行一些“包装”。由于时间关系,我将在不久的将来为大家献上关于CeRapiInvoke的详细说明。