题目中的信息是指很少量的数据。比如配置信息、注册表数据等。在其它Windows 操作系统中,这些信息一般都被保存在*.ini文件或注册表中。
嵌入式设备没有统一的要求。有的不需要保存任何数据,有的将数据保存到RAM中,在关闭设备时仍然给RAM提供电源。有的将数据保存到永久存储设备中。不同的情况有不同的解决方案,这就造成了在保存信息方面,相关的技术很繁杂,要弄明白需要时间和实践。
在这篇文章中我主要讲解在Windows CE下如何将数据保存到永久存储器中。将数据保存到永久存储器中应用广泛,不易丢失数据,可以保存大量的数据,也不必在系统关闭后还要提供电源。相比较将数据保存到RAM中要不间断地提供电源,存储空间也大大受限制。
保存到*.ini文件中
要想让你定制的内核支持永久存储设备,需要加入对永久存储设备的支持(就是驱动程序),再加入一种文件系统。现在假如我们的存储硬件采用IDE接口,使用一种简单实用的文件系统,FAT是合适的选择。那么对应的操作步骤如下:先使用新内核生成向导,生成一个内核工程后(采用哪个平台模板都行),在PB右边“catalog”窗口中加入对存储设备的支持。在PB v4.1下具体位置为“device drivers”-“storage devices”-“ATAPI PCI/IDE Storage Block Driver”。单击右键,在弹出菜单中单击“add platform”,PB立刻将相关驱动程序源码或者编译后的文件,还有相关环境变量加到你的内核工程中。之后再加入FAT文件系统驱动。FAT组件的具体位置为:
“Core OS”-“display based devices”-“file system and data store”-“storage manager”-“FAT File System”。
也用同样方法加入到左边内核工程中。之后设置环境变量。方法是单击PB菜单“platform”-“settings”-“environment”- “new”,输入环境变量名“IMGRAM64”,再输入值“1”。准备工作完成后,编译整个内核。你定制的内核就应该支持永久存储了。
先不考虑注册表问题。假如我们的产品不需要保存注册表数据,但是需要将一些软件使用的配置信息保存到永久存储器中,那么采用ini文件方式比较适合。不采用注册表保存数据的原因是在定制内核时需要进行复杂的设置,来保证内核能在每次启动时加载保存到永久存储设备上的注册表数据。如果只是几个软件需要使用配置信息,那就没必要为了能够保存注册表数据而设置内核。把话说简单一点:除非必要,能不用注册表保存最好。
虽然在EVC帮助文件中提到了MFC库中有CWinApp::WriteProfileInt和CWinApp::WriteProfileString 。而且在EVC下编译也通过了。但实际执行起来函数返回值为0,说明没有任何数据被写到硬盘上。在硬盘或者\windows目录下也没找到ini文件。这看起来很蹊跷,如果你使用EVC开发过,你就见怪不怪了。下面顺便说说我遇到过的“蹊跷”:
A、有的函数在帮助文件中提到过,说CE支持此函数。但在编译时EVC不识别,或者链接时不识别。一种原因可能是你在安装了CE和EVC后,没有调整 EVC的编译和链接目录,也就是包含头文件和库文件的目录。EVC在安装后,默认包含头文件和库文件的目录为Standard SDK目录,Standard SDK是专为模拟器准备的,自然内容很少,找不到某个函数也在情理中。解决办法是修改EVC的包含头文件和库文件的目录。位置在菜单“tools”- “options”-“directories”。在“CPUs”栏里选择你的CPU类型,然后分别添加头文件和库文件的目录,添加的头文件目录和库文件目录是安装Windows CE时SDK的目录。举例来说,假如开发平台使用的是x86 CPU。那么在“CPUs”中选择“Win32 (WCE x86)”,然后在“Show directories”中选择“include files”,接着在下面“Directories”栏里添加几个目录。如图1所示(SDK指软件开发包,OAK指OAL开发包,DDK指驱动开发包)。接着在“Show directories”中选择“library files”,把库文件目录也添加进去。如图2所示。另一种原因就是真的没有此函数。CE的帮助文件做的并不好。很多函数的说明、例子代码都是照抄于VC 下的帮助文件。只有编译或者运行程序时你才知道这个函数是否存在。
注:如果你是在PB下创建工程编写程序,就不必设置了。
图一 |
图二 |
回到正题。CWinApp::WriteProfileInt和CWinApp::WriteProfileString函数既然不能写数据到永久存储设备。那就必须自己写这两个函数和对应的读取数据函数GetProfileString和GetProfileInt。因为这四个函数使用频率很高,故我将此函数写出来,当你需要的时候就不用再写了。
注:1、函数中所有字符串均为UNICODE。2、采用了MFC类库。3、具体问题见代码。
本文主要讲解Windows CE.NET下注册表相关技术。
1、 对象存储(object store)
对象存储是Windows CE默认的数据存储机制。任何新创建的内核中都默认包含对象存储器。对象存储的实质是在RAM中创建一个文件系统,将文件保存在RAM中,这些文件来源于 ROM。当设备启动时,引导程序将ROM中的内核文件解压并存放在RAM中。"\windows"目录就是基于对象存储的。对象存储的特点是文件可以压缩、支持事务机制(和数据库中的事务机制相似)、数据I/O相对较快。
A、对象存储中的对象类型包括文件、目录、数据库、记录、数据库卷。CE为每个对象分配一个对象ID(CEOID)。访问或者操作任何对象的前提是得到该对象ID。
B、CE能够自动压缩所有对象存储中的文件(CE提供了一个选项供OEM设置是否能够自动压缩文件)。所以文件没有压缩或不压缩的标志,但是有一个标志,标明此文件存在于ROM还是RAM中。一个文件最大长度可达到4 GB。
C、CE提供了三种文件系统:基于ROM的文件系统、基于RAM的文件系统、FAT文件系统。
2、 注册表(registry)
CE下注册表和其它windows操作系统中注册表概念和结构基本相同。
A、CE下注册表限制:键名最大长度255个字符; 数据最大 4KB;子键深度最大值 16层。
B、根键有HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS。
C、操作注册表函数:
i. 打开RegOpenKeyEx 和创建RegCreateKeyEx
ii. 读RegQueryValueEx写RegSetValueEx
iii. 枚举入口或子键RegEnumValue、RegEnumKeyEx
iv. 删除入口或子键RegDeleteValue、RegDeleteKey
v. 关闭RegCloseKey
3、CE下注册表类型
CE下注册表类型分为基于RAM的注册表和基于HIVE的注册表。
A、基于RAM的注册表,也叫基于对象存储(oject storage)的注册表。用于将注册表数据全部保存在RAM中。
i. 从CE v1.0开始到CE .NET之前,仅采用此技术来保存注册表。每个新创建的内核都默认采用此技术来保存注册表。
ii. 适合频繁热启动而不冷启动的设备。系统关闭时提供低电源给RAM。如果断电,重新启动设备后,系统将从内核中重新读取注册表数据到RAM。当然以前保存的用户数据已经丢失。
iii. 基于RAM的注册表也能够永久保存注册标数据。CE提供了两个机制。
1) 第一种机制的设计思路是在设备关闭前调用RegCopyFile函数将整个注册表数据以文件形式保存到永久存储器上。重新启动设备时,调用 RegRestoreFile函数将文件全部读出到RAM中。但是这时必须一次热启动才能使恢复的注册表数据有效。所以每次启动就多出一次热启动。好在热启动非常快,几秒钟的时间。
2) 另一种机制可以避免前一种机制的需要两次启动的缺点。但也有它的缺点。OEM(原始设备制造商)可以在OAL层编写WriteRegistryToOEM and ReadRegistryFromOEM两个函数,内核在启动时会自动调用ReadRegistryFromOEM函数来读注册表数据。而应用程序调用 RegFlushKey函数时,这个函数用调用WriteRegistryToOEM函数写注册表数据到永久存储器上。这个机制避免了两次启动的缺陷。但问题出现在内核启动时,调用ReadRegistryFromOEM之前文件系统驱动程序还没加载,那就无法打开、读取文件。CE帮助文件中说解决办法是将从永久存储器中读取数据的代码加到ReadRegistryFromOEM中。帮助中说的意思可不是调用ReadFile这么简单的,因为文件系统驱动程 序还没加载。
3) 个人建议:如果要采用基于RAM的注册表保存机制,而且要求永久保存注册表数据,使用第一种机制比较容易。
B、 基于HIVE的注册表。用于将注册表数据全部或部分保存到永久存储器上。
i.它是从CE.NET开始采用的新技术。适合经常冷启动而不热启动的设备。
ii.支持多用户信息分别保存。当一个用户登录时,加载这个用户的注册表数据,注销时卸载这个用户的注册表数据。
iii. HIVE是指一组键,包括子键、键值、数据。是保存或者加载注册表数据的单位。分为系统HIVE(system hive)、用户HIVE(uer hive)、引导HIVE(boot hive)。
1) 系统HIVE包含了关于系统的设置信息。具体保存注册表中HKEY_LOCAL_MACHINE、HKEY_CLASSES_ROOT、 HKEY_USERS键下所有数据。保存系统HIVE的文件的路径在【HKEY_LOCAL_MACHINE\init\BootVars】下,键名为 "systemhive",键值为文件的路径。默认为"\Documents and Settings\system.hv"。
2) 用户HIVE包含了一个用户的信息。具体保存注册表中HKEY_CURRENT_USER键下所有数据。保存用户HIVE的文件的路径同样为【HKEY_LOCAL_MACHINE\init\BootVars】下,键名为"profiledir",键值为所有用户HIVE的共同目录。默认为 "\Documents and Settings",在这个目录下包含了以每个用户名命名的子目录。子目录里含有一个文件,默认文件名为user.hv。
3) 引导HIVE保存在ROM(内核)中。具体保存内容同系统HIVE一样。当解压内核并加载注册表时,系统先将引导HIVE数据读出,引导HIVE包含了永久存储器的驱动程序和文件系统的驱动程序,这些驱动加载后,系统HIVE被加载,然后引导HIVE被释放。因为引导HIVE被包含在内核中,所以存在一种情况:如果重新做了一个新内核,引导HIVE中的数据同系统HIVE可能不相同。那么系统该加载哪个版本好呢?为此,CE在生成每个内核时都做了一个标志。而系统HIVE也存在这样一个标志,当加载引导HIVE时,如果引导HIVE和系统HIVE的标志不相同,系统会删除系统HIVE文件,然后重新创建一个文件并从引导HIVE复制数据。
iv. 永久保存注册表数据
Windows CE.NET采用新的注册表保存技术――基于HIVE的注册表,的确让人很兴奋,在这之前基于Windows CE的设备,大多数采用给RAM供电方式来保存注册表数据,虽然也可以通过RegCopyFile函数永久保存,但毕竟启动时还要再热启动一次,有了基于 HIVE的技术,启动时系统会自动加载数据,免去了热启动的麻烦,而且当内核更新升级时,你不用担心保存在永久存储器上的系统HIVE文件影响你新的内核,系统会自动判断并删除过时的系统HIVE文件。只有拥有了这样的技术,基于CE的产品才算是一个真正的电脑。
注:关于基于HIVE的注册表的实现,暂不讲解。
下面简单说明 RegCopyFile和RegRestoreFile的用法。
///使用基于RAM的注册表,利用RegCopyFile和RegRestoreFile ///实现永久保存注册表数据。 #include <Pkfuncs.h> #include <winbase.h> CString strRegBackup = L"\\hard disk\\RegBackup.reg"; CString strTmp = L"\\windows\\temp.reg"; ////////导出。在系统关闭前。 if(! RegCopyFile(strTmp)) ///导出注册表,用temp.reg做缓冲用。 { return FALSE; } if(! CopyFile(strTmp, strRegBackup, FALSE)) ///把temp.reg再复制到RegBackup.reg { return FALSE; } /////////导入。在系统启动时。 if(! CopyFile(strRegBackup, strTmp, FALSE)) { return FALSE; } if(! RegRestoreFile(strTmp)) ///恢复注册表 { return FALSE; } if(! KernelIoControl(IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL)) ///重新启动 { return FALSE; } |
付林林:毕业时间:2001年,专业:计算机。从毕业起一直从事软件开发工作。目前从事 Windows CE 下操作系统内核定制和应用程序开发。在两年的时间里积累了CE下开发的一点点经验。希望和 CE 下开发者交流、探讨,更希望你们能不吝赐教。