前提条件
要成功生成此示例,需要:
Visual Studio .NET 2003
Microsoft Platform SDK 的 Microsoft 安装程序部分(http://www.microsoft.com/msdownload/platformsdk/sdkupdate/[英文])
用于 Pocket PC SDK 的 Windows CE .NET 或 Windows Mobile(用于 Pocket PC SDK 的 Windows Mobile 2003:http://www.microsoft.com/downloads/details.aspx?familyid=9996b314-0364-4623-9ede-0b5fbb133652&languageid=f49e8428-7071-4979-8a67-3cffcb0c2524&displaylang=en%20[英文])。
如果希望修改 SetupHelper.dll 文件,则需要使用适当的 Platform Builder/Embedded Visual C++ 工具来为所有相应的平台进行编译。
默认情况下 Makefile 配置为直接从 Visual Studio 目录中拖动 .cab 文件,尽管 Makefile 是为.NET Compact Framework 的 SP1 编写的(SP1 将 ARMV4T cab 替换为 ARMV4I)。为方便起见,示例使用的 SetupHelper.dll 文件包括为 .NET Compact Framework 支持的所有平台编译的二进制文件。
检测 Microsoft .NET Compact Framework
需要创建一个可以从 MSI 文件作为自定义操作调用的 .dll 文件。
首先使用 CeRapiInitEx 连接到设备。
然后使用 CeFindAllFiles 在设备的 Windows 目录中搜索 GAC_mscorlib_*.dll。如果存在,表明设备上安装了某种类型的 .NET Compact Framework。
查询设备注册表并枚举 HKLM\SOFTWARE\Microsoft\.NETCompactFramework 中的值。将会找到表示 NETCF 的当前版本的注册表键(如 1.0.2268.00:REG_DWORD:0)。这个版本由所有 .cab 安装创建,并将包括在以后所有的 ROM 安装中。在 RTM .NET Compact Framework 的初始 ROM 安装会丢失此键,所以如果没有找到此键,则假定其值为 1.0.2268.0。
此时,您就可以知道 .NET Compact Framework 是否存在,以及是否需要将自己的版本部署到此设备。如还需部署 SQL CE 文件,可以使用类似的机制在 Windows 目录下搜索 GAC 库。SQL 安装不向注册表发布其版本信息,因此获得此生成版本的唯一途径便是读取 .dll 文件的 Win32 资源。在示例中,我选择始终安装 SQL .cab(如果需要)。这样比较安全,因为 CE 安装程序将防止将设备中的 SQL 软件包误降级。对于.NET Compact Framework 也可以采用类似的方法。
检测设备类型
要成功地选择要部署到设备的正确 .cab,您将需要以下信息:Windows CE OS 版本、平台字符串、处理器类型和指令集。目前,无法通过 RAPI 直接获得以上所有信息,因此获取这些信息的唯一途径便是将本机“SetupHelper.dll”文件下载到设备,并通过 CeRapiInvoke 调用它。这有点象母鸡和蛋的问题,如何才能知道要下载哪种本机 .dll 来确定需要哪种 .cab 呢?答案是把所有 .dll 文件都试一遍。如果 .dll 文件不能在设备上运行,则 CeRapiInvoke 将失败。由于这些 .dll 文件大小均为 4k 左右,因此依次尝试每个文件是一个比较迅速的操作,并且一般尝试头几个文件即可。以下给出了确定正在处理哪种平台的步骤:
CeGetVersionEx 将给出 Windows CE OS 的版本信息。
如果该设备为 Windows CE 3.0,可通过 SetupHelper.dll 检索平台字符串。.NET Compact Framework 将只能在基于 Windows CE 3.0 的 PocketPC 上运行,并且 PocketPc 将包含平台字符串“Palm PC2”或“PocketPC”。如果为任何其他值,则表明 Windows CE 3.0 不支持此平台。
如果设备为 Windows CE 3.0 和 PocketPC,请调用 CeGetSystemInfo 并使用 dwProcessorType 字段选择相应的平台:
基于 Windows CE 3.0 的 PocketPC 的 .cab 文件名为 netcf.core.ppc3.[Platform].cab,即 netcf.core.ppc3.arm.cab。
如果设备为 Windows CE 4.1 或更新版本,要正确选择适当的 .cab 文件,需要知道设备指令集(通过 SetupHelper.dll)。
Windows CE 4.1+ 的 .cab 文件名为 netcf.all.wce4.[Processor].cab。
部署到设备
要对设备调用 .cab 文件,必须调用 wceload.exe,并将此 .cab 文件的完整路径作为命令行参数,使用 CeCreateProcess 可轻松完成操作。不过,由于一般不需要同时调用多个 .cab 文件,因此需要等候 wceload 完成,并且不能与使用 CreateProcess 一样在进程句柄上使用 WaitForSingleObject。
这是另一个 SetupHelper.dll 能派得上用场的例子。可以对设备进行 WaitFor???... 调用并仅仅阻塞,但这样安装程序将依赖于设备的行为。可改为每隔一秒轮询一次,以确定进程是否结束。这样便可以依次调用需要的所有 .cab 文件,首先调用 .NET Compact Framework,然后调用两个 SQL CE .cab,最后调用应用程序。
但应注意,不能保证 wceload 无需干涉。特别是升级 .NET Compact Framework 的 ROM 安装时,设备上可能出现需要用户交互的对话框。因此,最好让 MSI 提醒用户注意设备的变化,或换用 SetupHelper.dll 监视 wceload 进程的弹出窗口,并使用 WIN32 API 按下相应的按钮。
在设备上 - Cab 和 GAC
作为补充,本示例演示了托管应用程序安装的另外两个方面:制作 cab 文件并安装到 GAC。
应用程序本身是一个简单的“Hello World”表单,但它包括一个德语附属程序集 .dll,并将其放到设备的全局程序集缓存中。在最初由 Visual Studio 生成时,附属程序集被放置在应用程序下的子目录中,名为 de\MyApp.resources.dll。通常无需将这样的 .dll 文件放在 GAC 中,因为 .NET Compact Framework 支持大多数与桌面相同的探测机制,并将搜索应用程序目录下已本地化的子目录以查找资源。为实现本示例的目的,创建的 .dll 文件相对较简单。
附属程序集 .dll 进行了完全签名。这是必须进行的操作,因为 .NET Compact Framework GAC 不支持延迟签名。文件名也从 de\MyApp.resources.dll 重命名为 MyApp.resources.de.dll。这仅仅是为了避免安装程序创建“de”目录。如果创建了此目录,则它将在 .dll 文件移至 GAC 后将被清空。一旦放入了 GAC,它将把最初的名称“MyApp.resources”作为严格名称的一部分。
然后,将创建 .gac 文件,该文件带有放置 .cab 文件和移动 .dll 文件的显式路径。我选择的是 \windows\,也可以是 \temp\ 甚至根目录。要紧的是此路径须一致,因此,编写 .gac 安装程序不像生成 .cab 文件那样支持使用 %CE#% 宏。
最后,生成 .cab 本身。本示例将 .dll 和 .gac 文件放入 \windows\(.gac 文件“必须”放入 %CE2% 指向的目录中),将应用程序放入 Program Files,并创建相应的快捷方式。