Win CE开发

来源:百度文库 编辑:神马文学网 时间:2024/05/01 19:19:04
Win CE开发特性及忠告
52RD.com 2005年9月18日
最近一段时间,移动设备开发越来越多的成为了程序员社区的话题。移动设备主要包括智能手机和PDA,是嵌入式开发中很重要的一个方向。在智能手机领域被大多数手机厂商支持的J2ME无疑是领头羊,微软CE平台的SmartPhone也逐渐成为关注焦点。一直不温不火的PDA市场,也在行业应用领域有所收获,Pocket PC由于其开发与Windows平台的一致性而得到了开发人员的青睐。
在长期关注程序员论坛的过程中,我发现由于Windows CE开发的独特性,加之多个版本并存、缺乏中文参考资料,所以论坛上充斥着大量相同的入门问题。我希望在这里能够为刚转入Windows CE开发的程序员明晰一些概念,将现有的Windows CE版本与开发工具之间的关系给大家解释清楚。
Windows CE与平台开发
Windows CE是微软为嵌入式设备打造的操作系统,而嵌入式设备可谓多种多样,这就要求CE操作系统必须是可定制的,所以微软将Windows CE设计为模块化的操作系统。说简单点,我们可以把Windows CE想像成一盒积木,你可以用积木搭建出任何物体,但不一定要把所有的积木都用上。
Windows CE搭建出来的物体就是平台,是适应某种有固定标准的嵌入式设备的操作系统子集,最著名的平台就是Pocket PC了,是提供给没有键盘的掌上电脑使用的平台。由于平台和硬件的一致性,所以有时候我们也用平台的名称来称呼整个系统——硬件与操作系统的总和。
我们也可以自己开发平台,开发工具是微软提供的Platform Builder,Platform Builder的版本号是和Windows CE的版本号一致的。
更多程序员关心的是应用程序的开发,而应用程序开发是针对特定平台的,我们在开发之前必须安装目标平台的SDK,才能够开发出适应目标平台的开发工具。
Windows CE开发环境综述
初学者另外一个比较糊涂的概念是版本的问题,现在市面上能够见到Windows CE的两代产品,它们的内核分别基于Windows CE 3.0和Windows CE.NET(即4.0)。
微软将今年刚面世的Pocket PC 2003和Smart Phone 2003统称为Windows Mobile 2003,我们大多数时候还是习惯地沿用老称谓。
而市面上经常见到的Pocket PC 2002是基于Windows CE 3.0的平台,而Pocket PC 2003则是基于Windows CE.NET的平台,需要注意的是,Pocket PC 2003的内核是Windows CE.NET 4.2。而SmartPhone2003也是基于Windows CE.NET的。SmartPhone的最初版本是2002,基于Windows CE 3.0的,但是微软没有推出SmartPhone2002的中文版。
清晰了平台与CE之间的关系,解释平台与开发工具之间的关系就很容易了。微软提供给应用程序开发者的工具包括:Embedded Visual Tools 3.0,其中包括Embedded Visual C++ 3.0和Embedded Visual Basic 3.0;Embedded Visual C++ 4.0和Visual Studio.NET。
开发工具的版本号是与Windows CE的版本号对应的。EVC3.0和EVB3.0是用来开发基于Windows CE 3.0平台的应用程序的,比较常见的平台有:Pocket PC 2002、Pocket PC 2000、Palm-size PC、HPC。而EVC4.0是用来开发Windows CE.NET平台的程序的,主要包括Pocket PC 2003和SmartPhone 2003。
Visual Studio.NET针对嵌入式设备开发需要SDE的支持,而VS.NET 2003中包括了SDE,不需要另外安装。Visual Studio.NET开发的程序需要目标平台支持.NET Compact Framework。现在支持.NET Compact Framework的平台有Pocket PC 2002和Pocket PC 2003。这里需要注意的是SmartPhone 2003是不支持.NET Compact Framework的。
EVB开发入门
微软已经宣布EVB不再支持Windows CE.NET,所以EVB的最终版本是3.0。但由于EVB的易上手性和快速开发的特点,在VS.NET横空出世之前,它成为Windows CE平台上快速开发的不二之选。现在EVB仍然适合Windows CE 3.0平台上小型应用程序的快速开发。如果您不是专职的Windows CE程序员,而只是需要在Windows CE平台上开发整个系统的一部分,那么EVB可以让您用很短的时间开发出您想要的程序。
EVB的开发环境的搭建也是十分简单,您可以从微软的网站上下载EVT 2002,其中包含了EVC 3.0、EVB 3.0和Pocket PC 2002 SDK和SmartPhone 2002 SDK。按照提示将EVB和Pocket PC 2002 SDK安装好后就可以进行开发了。SDK中包含模拟器,在没有实际设备的情况下,可以利用模拟器来调试程序。
这里需要注意的是,开发环境和模拟器之间是通过网络连接协议进行通讯的,所以开发所用的计算机上必须有一个活动的网络连接。如果没有,可以安装微软的虚拟网卡。
EVB的开发环境与VB类似,因为Windows CE应用程序需要在模拟器或者实际设备上调试,所以我们必须选择程序的输出目标。如果您选择了Emulation,在您按下运行(或F5)后,EVB将自动启动模拟器,并把程序下载到模拟器中。
由于新的Windows CE.NET将不再支持EVB,微软建议EVB程序员使用VB.NET开发新的程序,而对于原有的EVB程序也给出了迁移路径,关于这方面的论述,您可以参考MSDN文章《Moving from eMbedded Visual Basic to Visual Basic .NET》。
EVC开发入门
无论是Win32平台还是WinCE平台,Visual C++都是一个强大的开发工具。而EVC也是WinCE上的主流开发工具。EVC支持MFC类库的子集,可以给开发者提供最强大的支持,也使Win32平台上的VC程序员可以很容易地迁移到WinCE平台上。但由于MFC类库需要一个DLL,所以对某些存储空间有限的嵌入式设备来说,这是个很大的负担,所以SmartPhone就不支持MFC。
说这么多,让我们来创建一个EVC的工程。是不是和VC很像,需要提醒大家注意的是,由于嵌入式设备支持的CPU种类很多,我们在选择创建工程类型的同时,也要把该工程所支持的CPU类型选择好。创建工程的过程和VC是一样的。当然不同的平台支持的工程类型是不同的,比如Pocket PC 2003有支持MFC和API的两种工程,而SmartPhone 2003则只有支持API的一种工程。
EVC中比VC环境中多了一行下拉菜单的选项,分别用来选择:工程、SDK、CPU类型和输出设备。以Pocket PC为例,在实际设备上调试应该选择Win32(WCE ARMV4)Debug ,而在模拟器上则需要选择Win32(WCE emulator)Debug。
VS.net开发入门
又来到我们的.NET时间了,我怎么说又?最近大家都被JAVA和.NET搞得头昏脑胀了吧?不管大家怎么吵,.NET Compact Framework对于手中缺少开发利器的嵌入式程序员无疑是一大福音。Visual Studio .NET 2003完全支持对移动设备的开发,好了,让我们开始一段奇幻的.NET之旅吧。
打开VS.net 2003,选File - New – Project,就打开了上面的界面。让我们来建立一个Visual C#的工程,然后选择Smart Device Application,然后OK。
你在这里要选择目标设备:Pocket PC、SmartPhone、Windows CE(指的是其他平台),下面则是选择创建的工程类型,我们选择“Windows Application”,左边是选择的平台所支持的模拟器。最后点击OK,我们就可以进入VS.NET的主界面了。
选择输出设备的情况和EVB十分类似,只需要选择输出设备,而不用选择CPU类型。当然了,因为.NET是运行在虚拟机上的了。在CPU类型众多的嵌入式领域,.NET和JAVA才能真正发挥自己的强项。
当然,我们也可以选择VB.NET作为开发智能设备的语言,情况和C#完全一样。目前智能设备开发只支持C# 和VB.NET。爱好C++的程序员可能还要等上一段时间。
Windows CE 开发的忠告
可以说当我们花了大部分时间将已有的应用程序移植到Microsoft Windows CE中。一般说来,这个计划不是太难。我们起步于Microsoft Win32代码,当然Windows CE是基于Win32应用程序接口(API)的。有利的是,我们的应用程序(即Raima 数据管理器)有方便的使用接口,并包含一个大约由150个子函数组成的库,这些函数都是由C语言写成,可以用来创建、管理和访问数据库。
按建立应用程序的方式来说,我们原以为将它移植到Windows CE中是一项相对简单的C语言编程练习。然而,我们不久便遇到好些困难。从粗心大意的错误开始,比如在基于Windows NT 的Windows CE仿真器上使用Microsoft Windows NT库,接着又违背Windows CE的编程戒律,如"千万不要给Unicode(国际标准组织10646标准)字符分配奇数内存地址"。
大约有百分之九十的问题或多或少地与Unicode有关。尽管Unicode编程不难,但是,当给单字节字符编写代码时,很容易出错(我有过许多次错误)。
下面这些忠告是根据我们在Windows CE上编写Raima 数据管理器的经验总结出来的,但我相信,在做任何其它Windows CE程序之前,它们都值得借鉴。毕竟大多数Windows开发者,当他们创建第一个Windows CE应用程序时,真正运用的是已掌握的Win32知识。
不要在仿真器上使用Windows NT库
这里所讨论的第一个错误实在太愚蠢了,但我还是陷了进去,也许你也会。当用Microsoft VC++(5.0版)创建一个Windows CE程序时,你会发现,包含路径(include)、 库路径(library)、及可执行程序路径被自动调整以匹配反应目标环境的选择。因此,比如说为Windows CE模拟器建立应用程序时,你会发现,include路径没有指向Win32的包含文件(在VC目录下),而是指向Windows CE包含文件(在WCE目录下)。千万别去修改。
由于Windows CE在Windows NT下运行,所以仿真器上运行的程序能够调用任一Windows NT动态链接库(DLL)中的函数,即使这个DLL不是模拟器的成员也一样。显然,这不是很好的事,因为相同的函数也许在手持PC(H/PC)或Windows CE设备上不可用,而你的软件最终要能在这些设备上运行。
第一次将非Unicode应用程序装入Windows CE仿真器时,你会发现,许多正在使用的函数它都不支持,例如美国国家标准协会(ANSI)定义的字符函数strcpy()。这也许引诱你去链接Windows NT 运行时间库,以便能解决所有问题。
如果你是刚开始用Windows CE编程,可能你能用的包含文件和库文件是明显的。答案就是,你不要采用那些在写普通Win32或非Windows CE程序时使用的包含文件和库文件。
不要混淆TCHARs和bytes
如果你正在Windows CE上写非Unicode应用程序,你或许要将所有的字符串从单个字符(chars)转换为宽字符(widechars)(例如,C变量类型whcar_t)。几乎所有Windows CE支持的Win32和运行时间库函数都要求宽字符变量。Windows 95不支持Unicode,然而,为了使程序代码具有可移植性,你要尽可能采用tchar.h中定义的TCHAR类型,不要直接使用wchar_t。
TCHAR是定义为wchar_t还是char,取决于预处理器的符号UNICODE是否定义。同样,所有有关字符串处理函数的宏,如_tcsncpy宏,它是定义为Unicode函数wcsncpy还是定义为ANSI函数strncpy,取决于UNICODE是否定义。
在现存的Windows应用程序中,有些代码也许暗示字符长为单字节。这在给字符串分配内存时经常用到,例如:
int myfunc(char *p)
{
char *pszFileName;
pszFileName = malloc(MAXFILELEN);
if(pszFileName)
strncpy(pszFileName, p, MAXFILELEN);
/*etc*/
在这段代码中,分配的内存块应该写作(MAXFILELEN * sizeof(char)),但是大多数程序员喜欢将它简化为MAXFILELEN,因为对于所有的平台来说sizeof(char)的值等于1。然而,当你用TCHARS代替多个字符时,很容易忘记这种固有的概念,于是将代码编写成下面的形式:
int myfunc(TCHAR *p)
{
TCHAR *pszFileName;
PszFileName = (TCHAR*)malloc(MAXFILELEN);
If (pszFileName)
tcsncpy(pszFileName, p, MAXFILELEN);
/*etc*/
这是不行的。它马上会导致出错。这里的错误在于malloc函数中指定变量大小为bytes,然而_tcsncpy函数中使用的第三个变量却指定为TCHARs而不是bytes。当UNICODE被定义时,一个TCHAR等于两个字节数(bytes)。
上述代码段应该改写为:
int myfunc(TCHAR *p)
{
TCHAR *pszFileName;
PszFileName = (TCHAR*)malloc(MAXFILELEN * sizeof(TCHAR));
if(pszFileName)
tcsncpy(pszFileName, p, MAXFILELEN);
/*etc*/
不要将Unicode 字符串放入奇数内存地址
在Intel系列处理器上,你可以在一奇数内存地址储存任何变量或数组,不会导致任何致命的错误影响。但在H/PC上,这一点不一定能行 ? 你必须对大于一个字节的数据类型小心谨慎,包括定义为无符号短型(unsigned short) 的wchar_t。当你设法访问它们的时候,将它们置于奇地址会导致溢出。
编辑器经常在这些问题上提醒你。你无法管理堆栈变量地址,并且编辑器会检查确定这些地址与变量类型是否相匹配。同样,运行时间库必须保证从堆中分配的内存总是满足一个word边界 ,所以你一般不必担心那两点。但是,如果应用程序含有用memcpy()函数拷贝内存区域的代码,或者使用了某种类型的指针算术以确定内存地址,问题也许就出现了。考虑下面的例子:
int send_name (TCHAR * pszName)
{
char *p, *q;
int nLen=(_tcslen(pszName) + 1) * sizeof(TCHAR);
p=maloc(HEADER_SIZE + nLen);
if(p)
{
q = p + HEADER_SIZE;
_tcscpy((TCHAR*)q, pszName);
}
/* etc */
这段代码是从堆中分配内存并复制一个字符串,在字符串的开头留一个HEADER_SIZE的大小。假设UNICODE定义了,那么该字符串就是一个widechar字符串。如果HEADER_SIZE是一个偶数,这段代码就会正常工作,但如果HEADER_SIZE为奇数,这段代码就会出错,因为q指向的地址也将为奇数。
注意,当你在Intel系列处理器中的Windows CE仿真器上测试这段代码时,这个问题是不会发生的。
在这个例子中,只要确保HEADER_SIZE为偶数,你就可以避免问题的发生。然而,在某些情况下你也许不能这么做。例如,如果程序是从一台式PC输入数据,你也许不得不采用事先定义过的二进制格式,尽管它对H/PC不适合。在这种情况下,你必须采用函数,这些函数用字符指针控制字符串而不是TCHAR指针。如果你知道字符串的长度,就可以用memcpy()复制字符串。因此,采用逐个字节分析Unicode字符串的函数也许足以确定字符串在widechars中的长度。
在ANSI和Unicode字符串之间进行翻译
如果你的Windows CE应用程序接口于台式PC,也许你必须操作PC机中的ANSI字符串数据(例如,char字符串)。即使你在程序中只用到Unicode字符串,这都是事实。
你不能在Windows CE上处理一个ANSI字符串,因为没有操纵它们的库函数。最好的解决办法是将ANSI字符串转换成Unicode字符串用到H/PC上,然后再将Unicode字符串转换回ANSI字符串用到PC上。为了完成这些转换,可采用MultiByteToWideChar()和WideCharToMultiByte () Win32 API 函数。
对于Windows CE 1.0的字符串转换,劈开(hack)
在Windows CE 1.0 版本中,这些Win32API函数还没有完成。所以如果你想既要支持CE 1.0又能支持CE 2.0,就必须采用其它函数。将ANSI字符串转换成Unicode字符串可以用wsprintf(),其中第一个参数采用一widechar字符串,并且认识"%S"(大写),意思是一个字符串。由于没有wsscanf() 和 wsprintfA(),你必须想别的办法将Unicode字符串转换回ANSI字符串。由于Windows CE 1.0不在国家语言支持(NLS)中,你也许得求助于hack,如下所示:
/*
Definition / prototypes of conversion functions
Multi-Byte (ANSI) to WideChar (Unicode)
atow() converts from ANSI to widechar
wtoa() converts from widechar to ANSI
*/
#if ( _WIN32_WCE >= 101)
#define atow(strA, strW, lenW) \
MultiByteToWidechar (CP_ACP, 0, strA, -1, strW, lenW)
#define wtoa(strW, strA, lenA) \
WideCharToMutiByte (CP_ACP, 0, strW, -1, strA, lenA, NULL, NULL)
#else /* _WIN32_WCE >= 101)*/
/*
MultiByteToWideChar () and WideCharToMultiByte() not supported o-n Windows CE 1.0
*/
int atow(char *strA, wchar_t *strW, int lenW);
int wtoa(wchar_t *strW, char *strA, int lenA);
endif /* _WIN32_WCE >= 101*/
#if (_WIN32_WCE <101)
int atow(char *strA, wchar_t *strW, int lenW)
{
int len;
char *pA;
wchar_t *pW;
/*
Start with len=1, not len=0, as string length returned
must include null terminator, as in MultiByteToWideChar()
*/
for(pA=strA, pW=strW, len=1; lenW; pA++, pW++, lenW--, len++)
{
*pW = (lenW = =1) ? 0 : (wchar_t)( *pA);
if( ! (*pW))
break;
}
return len;
}
int wtoa(wxhar_t *strW, char *strA, int lenA)
{
int len;
char *pA;
wchar_t *pW;
/*
Start with len=1,not len=0, as string length returned
Must include null terminator, as in WideCharToMultiByte()
*/
for(pA=strA, pW=strW, len=1; lenA; pa++, pW++, lenA--, len++)
{
pA = (len==1)? 0 : (char)(pW);
if(!(*pA))
break;
}
return len;
}
#endif /*_WIN32_WCE<101*/
这种适合于Windows CE 1.0的实现办法比使用wsprintf()函数要容易,因为使用wsprintf()函数更难以限制目标指针所指向的字符串的长度。
选择正确的字符串比较函数
如果你要分类Unicode标准字符串,你会有以下几个函数可供选择:
wcscmp(), wcsncmp(), wcsicmp(), 和wcsnicmp()
wcscoll(), wcsncoll(), wcsicoll(),和wcsnicoll()
CompareString()
第一类函数可用来对字符串进行比较,不参考当地(Locale)或外文字符。如果你永远不想支持外文,或者你仅仅想测试一下两个字符串的内容是否相同,这类函数非常好用。
第二类函数使用现有的当地设置(current locale settings)(系统设置,除非你在字符串比较函数之前调用了wsetlocale()函数)来比较两个字符串。这些函数也能正确分类外文字符。如果当地的字符"C"("C" locale)被选定,这些函数与第一类函数就具有了相同的功能。
第三类函数是Win32函数CompareString()。这个函数类似于第二类函数,但是它允许你指定当地设置(the locale)作为一个参数,而不是使用现有的当地设置(current locale settings)。CompareString()函数允许你选择性地指定两个字符串的长度。你可以将第二个参数设置为NORM_IGNORECASE,从而使函数比较字符串时不比较大小写。
通常,即使不将第二个参数设置为NORM_IGNORECASE,CompareString()函数也不用来区分大小写。我们经常用wcsncoll()函数来区分大小写,除非使用当地的字符"C"("C" locale)。所以,在我们的代码中,不使用CompareString()函数来区分大小写,而用wcsncoll()函数来区分大小写
不要使用相对路径
与Windows NT不一样,Windows CE没有当前目录这个概念,因此,任何路径只是相对于根目录而言的。如果你的软件给文件或目录使用相对路径,那么你很可能把它们移到别的地方了。例如,路径".\abc"在Windows CE中被当作"\abc"看待。
移走了对calloc()和 time()函数的调用
C运行库中的calloc()函数不能使用,但是malloc()函数可以代替calloc()函数。并且不要忘记,calloc()函数初始化时分配的内存为零,而malloc()函数不一样。同样,time()函数也不能使用,但你可以使用Win32函数GetSystemTime()函数代替time()函数。
经过以上的警告后,你会高兴地学习最后令你惊讶的两点忠告。
不需要改变Win32 输入/输出(I/O)文件的调用
Win32的输入输出函数,Windows CE也支持。允许你象访问Win32文件系统那样访问对象。CreateFile()函数在Windows CE中不能辩认标志FILE_FLAG_RANDOM_ACCESS%
WinCE中OEM适配层点滴之系统初始化
开发者在线 Builder.com.cn 更新时间:2007-12-28
付林林:
2001年计算机专业毕业。从毕业起一直从事软件开发工作。目前从事 Windows CE 下操作系统内核定制和应用程序开发。在实际工作中积累了CE下开发的一些经验。希望和 CE 下开发者交流、探讨,更希望你们能不吝赐教。我的EMail:windowsce@tom.com
天极网嵌入式开发论坛
进入作者专栏
正文
OAL(OEM Adaptation Layer)既OEM 适配层,从逻辑上讲位于Windows CE内核和硬件之间,从物理上讲OAL各个模块代码被编译后(.lib)和其它内核库链接到一起形成Windows CE的内核可执行文件nk.exe。Windows CE内核在OAL层暴露了大量的函数和全局变量,利用这些函数和全局变量OEM可以编写中断处理、RTC、电源管理、调试端口、通用I/O控制代码等。图1更直观地描述了OAL的结构。CE安装目录的子目录中包含了OAL的部分源码,大多数情况下开发者对OAL只要修改即可,甚至无需修改。通过阅读本篇文章,开发者能够了解OAL的结构、暴露的接口的功能,可以在此基础上实现甚至增强OAL的功能。

图1 OAL结构图
因为OAL层代码大多数和CE启动时系统初始化工作有关,所以本篇文章以CE的启动顺序为线索。其它OAL知识在下一篇文章中讲解。
一、在Boot Loader解压CE内核镜像文件(nk.bin)后开始跳转到StartUp(),StartUp函数属于OAL层,此时CE操作系统内核还没有运行。StartUp函数的功能主要有两个,一是初始化CPU为已知状态(known state),二是调用内核初始化函数(x86平台为KernelInitialize,其它平台为KernelStart)。初始化CPU工作因CPU的不同而不同,如果是ARM系列,包括设置CPU为管理员模式、禁止IRQ和FIQ、禁止MMU、清空指令和数据缓冲、检测启动原因、配置GPIO和内存控制器、初始化RTC、保存OEMAddressTable地址等。执行完毕后调用KernetStart。如果是x86系列,包括设置CPU为保护模式、初始化内存控制器、保存OEMAddressTable地址等。执行完毕后调用KernetInitialize。
二、内核初始化函数的功能也因CPU的不同而不同,不过有一些功能是相同的,如初始化串口(为了输出调试信息)、调用OEMInit函数等。对于x86系列,初始化工作除了上述的功能外还包括读取OEMAddressTable内容、确定分页大小、内核重定位、初始化中断分配表、初始化分页表、内存初始化和其它初始化。对于其它系列CPU请参考CE帮助文档。
1. 串口调试:
串口调试函数包括OEMInitDebugSerial、OEMReadDebugByte、OEMWriteDebugByte等。从OEMInitDebugSerial的源码可以看出,系统从BOOT_ARG_PTR_LOCATION为首地址的结构中判断当前连接的串口是哪个,然后配置这个串口。如果你的设备的串口I/O地址设置和CE默认的一致的话,就能在CE内核得到CPU控制权到启动完毕这段时间里通过串口得到调试信息。
2. OEMInit
一般在OEMInit中初始化所有外围的硬件、初始化系统时钟(system tick)和RTC(real time clock)、初始化KITL(Kernel Independent Transport Layer)。例如I486平台的OEMinit函数,它先关联所有的IRQ和中断ID,然后初始化PCI总线、网络适配器、电源管理、PIC(可编程中断控制器)、系统时钟,最后检测是否有扩展内存。另外如果OEM要通过OAL暴露的函数指针或者全局变量来增强功能的话,就要在此函数中实现(在下面详细讲解)。
3. 检测扩展内存
我们都知道在config.bib配置文件中设置CE系统使用RAM总量(如果不知道请参考我的文章Platform Builder之旅系列),注意这个RAM总量不是总的物理内存的大小。PB编译的内核包含一个变量ulRAMEnd,将在config.bib中定义的RAM的起始地址 + RAM大小的和赋值给ulRAMEnd。在CE内核的启动过程中,ulRAMEnd的值赋值给全局变量MainMemoryEndAddress,CE内核通过访问MainMemoryEndAddress得到RAM的总量信息。假如基于CE的设备附加了RAM,而MainMemoryEndAddress的值没有包括这段附加的RAM,结果CE内核无法知道已经附加了RAM。为了让CE内核了解附加RAM的信息,OEM应该编写一个函数检测RAM的总量,并把总量值赋给MainMemoryEndAddress。OAL暴露了一个函数指针pNKEnumExtensionDRAM,OEM应该把编写好的函数地址赋给这个函数指针。如果OEM不准备自己编写内存检测函数的话也可以调用OEMGetExtensionDRAM。从帮助文档中看出OEMGetExtensionDRAM这个函数能够检测内存的总量,但是CE的针对X86 平台的源码中没有具体编写这个函数的实现代码(见%_WINCEROOT%PUBLICCOMMONOAKCSPI486OALcfwpc.c)。也就是说在X86平台上调用OEMGetExtensionDRAM是检测不到RAM的。如果OEM有兴趣编写检测RAM总量的函数,可以调用现成的函数IsDRAM。这个函数也保存在cfwpc.c中。
三、内核初始化函数执行完毕后开始按如下步骤执行:
1. 内核创建用于与filesys.exe同步的事件对象SYSTEM/FSReady,之后启动filesys.exe。启动filesys.exe的意义是让filesys.exe读取注册表数据。
2. 内核等待事件SYSTEM/FSReady被触发,这个事件是由filesys.exe在做完一系列工作后触发。这一系列的工作内容如下:
2.1 先检测这是一次冷启动还是热启动,如果是冷启动,那么初始化对象存储内存区域。
2.2 调用OEMIoControl函数,I/O控制代码为IOCTL_HAL_INIT_RTC,也就是初始化RTC。
2.3 初始化数据库子系统和API、文件系统API、消息队列API。
2.4 如果操作系统镜像(nk.bin)包括RAM文件系统,那么读取Initobj.dat文件内容后创建一个RAM文件系统。
2.5 初始化注册表(在内存中形成注册表)。
2.6 如果此时device.exe没有启动,那么读取HKEY_LOCAL_MACHINESystemStorageManager下“Dll”的值(这个值为存储管理器所在的.dll的文件名)并加载到内存。加载之后创建一个线程专用于初始化存储管理器,初始化之后此线程结束。
2.7 初始化NLS(national language support)。关于NLS请参见我的文章《CE下中文输入法编辑器》。
2.8 为数据库引擎设置本地ID。
2.9 读取Initdb.ini文件,安装在对象存储中的数据库。
2.10 触发SYSTEM/FSReady事件,之后filesys.exe处于等待状态,等待内核发通知给它。
3. 此时注册表已经存在于内存当中,内核开始读取如下位置数据:
HKEY_LOCAL_MACHINELoaderSystemPath
HKEY_LOCAL_MACHINESYSTEMOOMcbLow and cpLow
HKEY_LOCAL_MACHINESYSTEMKERNELInjectDLL
HKEY_LOCAL_MACHINEMUIEnable and SysLang
HKEY_CURRENT_USERMUICurLang
4. 内核设置低内存处理(out of memory)。低内存处理是指当前可用的内存非常少时,内核所做的解决方案(CE帮助文档中有详细说明)。
5. 内核在做好了上述工作后通知filesys.exe,由filesys.exe做其余工作。filesys.exe所做的工作内容如下:
5.1 读取HKEY_LOCAL_MACHINESystemEvents 下包含的所有事件对象名称并一一创建。
5.2 读取HKEY_LOCAL_MACHINEInit 下包括的所有应用程序名称并一一启动。如果device.exe在列表中并且此时它已经启动了,那么触发SYSTEM/BOOTPHASE2事件,这会使device.exe重新读取注册表数据来完成最后的驱动程序初始化。
5.3 初始化时间区域(time zone)。
WinCE中的Flash分区和CheckSum点滴
开发者在线 Builder.com.cn 更新时间:2007-11-26
CheckSum是一种用于检查数据文件有没有发生变化的方法,对于一些重要的数据文件为了检查传输过程过程中有没有数据的损坏或丢失,常常会用到CheckSum算法。
WinCE中经常用到CheckSum的地方就是对即将烧写进Flash中的image文件进行校验,和烧写完对写入的数据进行完整性检查,一般这里的image有OSimage和UT的bin文件两种。
CheckSum的原理是把一个文件以二进制的方式打开,将里面所有的字节的值一个一个的累加起来,一直到最后一个字节,最后得到一个累加值,它就是我们要的CheckSum的结果。从CheckSum的这个特性可知数据值为0的字节是不会影响到最终的结果,这种特性我认为也是CheckSum的一个弱点,不能像MD5,SHA1等摘要算法一样基本上能反映出哪怕一个bit的改动,但是这个特性也给WinCE运行期间计算保存在FLASH上的image数据文件的完整性带来了方便。
为了从Flash中得到正确的CheckSum值必须先了解image在Flash中的烧写方式,这包括了解image文件内部是怎么组织的,Flash的分区和块的分配是如何进行的。
先以Sumsung的FLASH为例来分析一下Flash的分区大体原则:
WinCE的Flash分区大体分为Nand BootLoader(NBL)区,Binfs区和文件区,NBL区存放BootLoader和烧写Image的工具程序,Binfs分区存放MBR、image的XIPKERNEL.bin、Chain.bin和NK.bin等OS的数据。文件区一般格式化为FAT分区让WinCE上层的磁盘和分区管理程序管理。Flash的分区是由UT在烧入image的时候决定的,包括每个分区的起止块地址,分区的大小和类型等,Detail如下:
1)NBL区一般占10个块(128K/块)的大小,分区虽小但是却是最重要的部分,保存着UT的三大模块:NBL1(bootloader),NBL2(IPL,Init Program Loader)和NBL3(Upgrade Tools),其中NBL1和NBL2共同保存在FlASH的第一个block中,FLASH芯片在生产的时候厂商都会特别保障这些block的可靠性,特别是保存了最开始bootloader代码和IPL的第一块。按经验来讲,NBL的三个模块加起来一共大约400多K,其占用的10块的block=128K×10 byte的空间大部分是空余的,为了下面叙述方便,这里假设NBL3_END_BLCOK为NBL的最后的block编号。
2)Binfs分区紧接着BL分区,即CE_START_BLOCK=NBL3_END_BLCOK+1,然后一般会将Binfs分区的第一个块存放MBR,MBR在这里仅仅是个标志,不像PC的硬盘中的MBR主要用来保存分区表的信息和引导代码。所以Binfs分区中保存OS数据的起止block范围为CE_START_BLOCK+1到CE_START_BLOCK+CE_MAX_BLOCK为止,我接触的项目中其大小一般为250个块左右,大约等于30Mbytes,WinCE的image一般不会超过这个大小,如果需要可以在分区时加大它的大小。
3)Flash的文件分区就是将剩下的block模拟成为和硬盘,CF卡类似的块设备让WinCE加载成盘符使用。
现在回到正题:如何计算CheckSum。
1)UT的CheckSum计算
UT的bin文件是由bootloader.bin(NBL1),IPL.bin(NBL2)和UpgradeTools.bin(NBL3)这三文件打成的一个封包,然后用PC上的checksum工具计算出checksum值,我们的目的就是在WinCE起来后用AP能通过读Flash的NBL的三个分区并实时计算到这个值。
UT的bin文件最后会被完整的烧写到NAND Flash的编号为0到NBL3_END_BLCOK的block中(虽然会被分为三块烧,但是数据是完整的),具体占用多少block由bin文件的大小决定,剩余的空间会以0填满。虽然不知道bin文件具体的结尾的位置,但是知道剩余空间填0的这个特性后,我们就可以直接调用NAND Flash的驱动程序,而且可以使用轻量级的不带坏块管理的驱动代码直接去读0到NBL3_END_BLCOK的所有数据,然后把每个byte累加起来就能得到CheckSum的值了。
2)IMG的CheckSum计算
大家都知道,如果定义了MultiXIP region的话,WinCE的image用romimage编译出来会生成多个bin文件,这里假设我们在配置image的bib文件中定义了两个region:XIPKernel和NK,那么在执行romimage ce.bib 之后我们会得到XIPKernel.bin,NK.bin,Chain.bin三个文件,最后调用makebinfs生成一个ceimgb.nb0的image镜像文件,我们也会先用CheckSum工具对ceimgb.nb0进行运算得到其完整的CheckSum值。
烧写的过程和UT有两点不同:1)烧写内容选择上,UT的bin文件的所有数据会被烧入Flash,而IMG的镜像文件包含了一些不需要烧入的头信息,所以可能导致烧入Flash的数据不完整;2)烧写使用的NAND Flash的函数不一样,因为IMG烧写在FLASH中的位置位于普通的不受特殊保护的块区,所以要考虑到坏块的管理,所以在调用具体的读写接口的时候要使用较高一层的代码,拿samsung的flash驱动PoketStoreII为例,烧写UT时用的读写函数为NF_ReadPage ,而烧写IMG镜像使用的时STL_Read/Write。
第一个不同点决定了我们如果要能计算得到正确的IMG的CheckSum值则必须将没有烧入到Flash中的ceimgb.nb0的头部数据烧写到为保存IMG预留的并且没有被占用的Flash中,比如IMG预留空间的最后一块,块号为CE_START_BLOCK+CE_MAX_BLOCK。我们通过修改烧写IMG的代码,在烧写完IMG的三个bin的数据后把从0x0到0x248(记不太清楚,大概)的数据写到块CE_START_BLOCK+CE_MAX_BLOCK中。这样的话因为其他的空余空间被0填充,我们就可以调用STL_READ把从CE_START_BLOCK+1(+1是为了略过MBR块)到CE_START_BLOCK+CE_MAX_BLOCK数据全读取出来并且累加得到最后的CheckSum值。  WinCE开发中Boot Loader的点点滴滴
开发者在线 Builder.com.cn 更新时间:2007-12-28作者:付林林
我很庆幸在公司的产品开发过程中并没有受到Boot Loader带来的阻力,因为我们采用MSDOS+Loadcepc来启动CE操作系统。显然这样的幸运不是永远的,所以对Boot Loader应该有足够的研究和了解,做到未雨绸缪。
Boot Loader是定制Windows CE操作系统过程中一个重要的开发环节。Boot Loader的作用正如名字中的两个单词:Boot,既引导系统,如果基于CE的产品采用BIOS实现硬件初始化和配置,那么Boot Loader只需引导软件系统。如果没有采用BIOS,那么Boot Loader的作用还包括实现BIOS的基本功能;Loader,既加载操作系统,在整个系统正常启动后Boot Loader通过不同的方式加载CE的内核文件nk.bin。当Boot Loader把nk.bin解压到RAM后就把CPU控制权交给CE内核。x86平台的Boot Loader种类最多,下面就对x86平台的Boot Loader做一说明:
x86 ROM Boot Loader
又叫Rom Boot,记得以前写过的文章中提到了Rom Boot。Rom Boot 被设计存放在Flash/EEPROM中,也就是原来BIOS的位置,这样当上电后CPU到固定地址执行代码,也就是执行了Rom Boot包含的代码,它对整个硬件系统进行初始化和检测,并且支持通过网卡从远程机器上下载nk.bin或者从本地IDE/ATA 硬盘的活动分区中寻找nk.bin文件加载。Rom Boot的优点就是引导并且加载速度快,而且它自身完成了所有的操作,这样就不用BIOS、MSDOS,更不用Loadcepc了。缺点就是需要CE开发者读懂它的源码并修改。CE提供了Rom Boot的所有源码,读者可以查找标题为“x86 Source Organization”的帮助文档,在这个文档中列举了所有相关的目录及内容,另外还列举了四种网卡的驱动程序源码所在目录。
x86 BIOS Boot Loader
BIOS Boot Loader和MSDOS+Loadcepc两种方式差不多,BIOS Boot Loader只是不需要MSDOS操作系统,它仍然需要BIOS和FAT文件系统。下面讲一下采用BIOS Boot Loader的系统的引导顺序:系统上电后BIOS执行完硬件初始化和配置后,BIOS检查引导设备的启动顺序,如果引导设备是硬盘、CF卡、DOC(Disk-On-Chip)一类的存储设备,那么就加载这些存储器上的主引导扇区(Master Boot Sector)中的实模式代码到内存,然后执行这些代码。这里提到的代码被称为主引导记录(MBR)。MBR首先在分区表(同样位于主引导扇区)中寻找活动分区,如果存在活动分区,那么加载位于这个活动分区的第一个扇区上的代码到内存,然后执行这些代码。这里提到的活动分区的第一个扇区被称为引导扇区(Boot Sector)。引导扇区上的代码的功能是找到并且加载BIOS Boot Loader,BIOS Boot Loader再加载nk.bin。引导扇区的源码位于%_WINCEROOT%PublicCommonOakCspi486BiosloaderBootsector目录下。有一个现成的引导扇区镜像文件,它的路径为%_WINCEROOT%PublicCommonOakCspi486BiosloaderDiskimagesSetupdiskBsect.img 。而对于BIOS Boot Loader,CE提供了Setupdisk.144和Bootdisk.144两个文件,以“.144”为扩展名的文件的解压我在前面的文章中讲过了。这两个文件解开后都包含了引导扇区和Boot Loader的镜像文件。执行“mkdisk C:”批处理命令将这两个镜像文件写到磁盘上。mkdisk会设置Boot Loader的隐藏属性,这样在列出根目录下所有文件时不会显示Boot Loader的文件。
MSDOS+Loadcepc
这种方式非常简单,在MSDOS启动后再执行loadcepc.exe,让loadcepc加载nk.bin到内存后再把CPU控制权交给CE内核程序。loadcepc在前面的文章中已经讲过了。
下面根据一般的Boot Loader源码来分析一下Boot Loader的组成:
Boot Loader由两部分组成:OEM启动代码(OEM startup code)和主代码(main code)。OEM启动代码是最先执行的部分,它的功能是初始化内存寄存器、设置CPU频率、初始化高速缓存等。之后它跳转到主代码中执行。一般OEM启动代码都是用汇编编写。主代码一般用C语言编写,它负责其它所有任务,在执行的同时还能够将执行的相关信息显示在屏幕上。一般添加公司LOGO或者其它启动LOGO都在此修改。
主代码主要由几个部分组成:镜像下载代码,通过并口或者网卡来实现从远程计算机下载nk.bin;串口调试代码,包含对串口的读写函数,用户调用这些函数就可以通过串口在远程计算机和本地计算机之间通信;写flash代码,包含写镜像到flash的函数;硬件监控代码。
一般的Boot Loader的执行流程见下图:

上图中每个函数的功能如下:
StartUp() :CPU最先执行的函数。也就是启动代码。
BootLoaderMain() :先后调用KernelRelocate、OEMDebugInit、OEMPlatformInit、OEMPreDownload等函数。此函数源码文件路径为%_WINCEROOT%publiccommonoakdriversethdbgblcommon 。
OEMDebugInit() :初始化串口。
OEMPlatformInit() :执行特定平台的初始化工作,如时钟、一些驱动程序。
OEMPreDownload() :做下载前的准备工作。一般用于反馈给用户一些信息。
DownloadImage() :下载操作系统镜像到RAM或者Flash。
OEMLaunch() :负责启动镜像。
OEMReadData() :从远程计算机读取数据。
OEMMapMemAddr() :专用于写Flash时使用。因为写flash的速度非常慢,所以此函数将Flash镜像临时缓冲到RAM中。
OEMShowProgress() :从函数名就能看出。
OEMIsFLashAddr() :判断一个地址是否是Flash的地址。
OEMFinishEraseFlash() :判断是否完成了擦除Flash内容工作。
OEMWriteFlash() :写镜像到Flash。
OEMStartEraseFlash() :开始擦除Flash。
OEMContinueEraseFlash() :继续擦除Flash工作。
Going Mobile: How to Port Your Win32 Code to Windows CE
ByNancy Nicolaisen                                         Go to page: 1  2  3  4  Next  One winter day in 1986, while working in a federal research program in Alaska, drinking a cup of coffee and reading the latest issue of Dr. Dobbs, I learned of a technology that seemed to hold out the promise of solving a lot of problems for my colleagues. According to Dr Dobbs Editor Jon Erickson, Microsoft‘s Windows 1.0 was going to do great things for IBM PC users. "Hock the Volvo" he admonished, "and buy the Windows SDK right now!"
I took him at his word, and plunged ahead, though I was fortunate enough to hang on to the family station wagon. Inside six months I was totally immersed in Windows programming, and have been ever since. In the intervening years, I have written code for every version of Windows, written about writing Windows code for most of the serious programming journals (including Dr. Dobbs ), and published three books on Windows Programming. I can honestly say I never met a Windows I didn‘t like, and Windows CE is far and away my favorite so far. Here‘s why:
The Windows CE Opportunity
The Windows revolution of the early eighties did two things. First, it created a consistent, predictable, intuitive user interface. Over time this attracted a broad community of users, and by extension, created a public body of knowledge about how to use personal computers and software. Second, Windows interposed a layer of hardware abstraction that made peripheral devices independent of specific operating systems and thus dramatically stimulated the PC aftermarket. If you weren‘t active in personal computer programming before the advent of Windows, this may not mean much, but it was very important. It meant that a non-technical person could buy a computer, printer, and mouse, take them home, connect them -- and -- they worked. At the time, this was remarkable. The wealth and variety of cheap, interoperable peripherals we have today is a direct result of the maturation of the Windows "Plug and Play" concept.
Windows CE brings us to the brink of a similar revolution for handheld and palmtop computers. We are, so to speak, on the threshold of changes that are going to have as much or more impact on the way we live as did the personal computer revolution. Win CE has inherent technical momentum that none of the other handheld or palmtop competitors enjoy, because it leverages the huge base of Win 32 knowledge, skill and code. With a little savvy effort, Win 32 code moves quickly to Win CE. By porting existing code rather than starting from scratch, you‘ll incur less risk, deliver applications more quickly, and enjoy the reassuring prospect of a stable API in the future.
Finally, the best thing about Windows CE is that it‘s the perfect medium for individual creativity and innovation. Because Win CE applications must be small and lightweight, they lend themselves to the kind of projects a single person or a very small team can undertake. The devices themselves are fairly inexpensive, the SDK is an add-on to tools you probably already have and know well, and most likely at least some of your code will move across without any changes at all. In short, this time around, you can give it a try without hocking the Volvo.
Part I: Adapting Application Appearance to Windows CE
Every single thing we do in moving a Win 32 application‘s user interface to Windows CE is motivated by the fact that CE ( an acronym that stands for "Compact Edition") is small. It has an abbreviated API, is designed with low power devices in mind, has fierce memory and storage limitations, mandates tiny screens, assumes few to no peripheral devices, and offers relatively little in the way of second chances to errant code. In short, the job of porting Win 32 code to Windows CE could be summed up in the bumper sticker one sees so frequently:
"REDUCE, REUSE, RECYCLE"In this first installment, I am focusing exclusively on the tasks that will get a user interface up and running for your application in the least time. In many cases this involves trimming back the lush, repetitive feature set we have come to expect on the desktop. This may go against the grain of your prior experience. What you have to remember is that the physical limitations of Windows CE are real. You can run out of memory, and you can run out of power. This one rule is the key to making a transition from the desktop user interface to a CE style interface: In any trade-off situation, we always choose the design solution that makes the application smaller.
Analyzing the User interface of Your Win32 Application
Our object in this lesson is to start with a Win32 application and get a visible Windows CE facsimile of it up and running as quickly as possible. Notice that I said visible facsimile. Here‘s what I mean by this. As a first step, we are going to isolate the parts of the Win 32 application that have to do with its user interface and include only those in our new Windows CE project. Right now we don‘t want to even consider the parts of the application that encode its specific behaviors and functionality.
We do this for several reasons. First, we divide the porting problem into the smallest, simplest elements possible, because this is the fastest path to stable code. If you fully port one aspect of an application at a time, test it, stabilize it and document it, then you‘ll consistently move forward, building on this incremental success. Second, a typical Win32 app, even a fairly lightweight one, will contain tens if not hundreds of unsupported function calls, library references, and other baggage that would take too long to successfully prune away if we started with all of it. Third, and most important of all, as you move the user interface of your porting subject to the highly constrained Win CE environment, you will undoubtedly shed a great deal of the desktop application‘s feature set. Shedding features isn‘t bad. It‘s good, and its necessary. When you have the user interface finalized on the CE side, you will be in a much better position to decide what functionality it makes sense to port. Our objective here is a ported app that reflects the spirit of the Win CE environment and its Win32 origins: it should be a small, lightweight, intelligently distilled essence of the Win 32 app from which it evolved.
The Resource File Is A Road Map
If a Windows application has a user interface at all, most of it‘s significant details are recorded in the resource file. Figure 1 is a screenshot of a simple Win32 application that includes icon, accelerator, dialog, menu, and string table resources.
Let‘s dissect the resource file for this application and assess the relative portability of its user interface.

Figure 1 - A simple Win32 application.
resource_file.rc
//Microsoft Developer Studio generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS/////////////////////////////////////////////////////////////////////// Generated from the TEXTINCLUDE 2 resource.//#define APSTUDIO_HIDDEN_SYMBOLS#include "windows.h"#undef APSTUDIO_HIDDEN_SYMBOLS#include "resource.h"///////////////////////////////////////////////////////////////////#undef APSTUDIO_READONLY_SYMBOLS///////////////////////////////////////////////////////////////////// English (U.S.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32//////////////////////////////////////////////////////////////////////// Icon//// Icon with lowest ID value placed first to ensure application// icon remains consistent on all systems.IDI_RESOURCE_FILE ICON DISCARDABLE "resource_file.ICO"IDI_SMALL ICON DISCARDABLE "SMALL.ICO"/////////////////////////////////////////////////////////////////////// Menu//IDC_RESOURCE_FILE MENU DISCARDABLEBEGINPOPUP "&File"BEGINMENUITEM "Open", ID_FILE_OPENMENUITEM "Close", ID_FILE_CLOSEMENUITEM "Save", ID_FILE_SAVEMENUITEM "SaveAs", ID_FILE_SAVEASMENUITEM "Print", ID_FILE_PRINTMENUITEM "Print Setup", ID_FILE_PRINTSETUPMENUITEM SEPARATORMENUITEM "E&xit", IDM_EXITMENUITEM SEPARATORMENUITEM "Recent Files", ID_FILE_RECENTFILESENDPOPUP "More Menus"BEGINPOPUP "Submenu 1"BEGINMENUITEM "Item1", ID_SUBMENU1_ITEM1MENUITEM "Item2", ID_SUBMENU1_ITEM2ENDENDPOPUP "&Help"BEGINMENUITEM "&About ...", IDM_ABOUTENDEND/////////////////////////////////////////////////////////////////////// Accelerator//IDC_RESOURCE_FILE ACCELERATORS MOVEABLE PUREBEGIN"?", IDM_ABOUT, ASCII, ALTEND/////////////////////////////////////////////////////////////////////// Dialog//IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 230, 75STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENUCAPTION "About"FONT 8, "System"BEGINICON IDI_RESOURCE_FILE,IDC_MYICON,14,9,16,16LTEXT "resource_file Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIXLTEXT "Copyright (C) 2001",IDC_STATIC,49,20,119,8DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUPENDIDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 242, 201STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENUCAPTION "ResourceFile Example"FONT 8, "MS Sans Serif"BEGINDEFPUSHBUTTON "OK",IDOK,185,5,50,14PUSHBUTTON "Cancel",IDCANCEL,185,25,50,14EDITTEXT IDC_EDIT1,25,25,135,45,ES_AUTOHSCROLLLISTBOX IDC_LIST1,25,95,95,50,LBS_SORT |LBS_NOINTEGRALHEIGHT |WS_VSCROLL | WS_TABSTOPGROUPBOX "Radio Button Group",IDC_STATIC,150,90,75,55LTEXT "Scrolling Listbox",IDC_STATIC,25,80,100,10LTEXT "Edit Control",IDC_STATIC,25,10,125,10END#ifdef APSTUDIO_INVOKED/////////////////////////////////////////////////////////////////////// TEXTINCLUDE//2 TEXTINCLUDE DISCARDABLEBEGIN"#define APSTUDIO_HIDDEN_SYMBOLS\r\n""#include ""windows.h""\r\n""#undef APSTUDIO_HIDDEN_SYMBOLS\r\n""#include ""resource.h""\r\n""\0"END3 TEXTINCLUDE DISCARDABLEBEGIN"\r\n""\0"END1 TEXTINCLUDE DISCARDABLEBEGIN"resource.h\0"END#endif // APSTUDIO_INVOKED/////////////////////////////////////////////////////////////////////// DESIGNINFO//#ifdef APSTUDIO_INVOKEDGUIDELINES DESIGNINFO DISCARDABLEBEGINIDD_DIALOG1, DIALOGBEGINLEFTMARGIN, 7RIGHTMARGIN, 235TOPMARGIN, 7BOTTOMMARGIN, 194ENDEND#endif // APSTUDIO_INVOKED/////////////////////////////////////////////////////////////////////// String Table//STRINGTABLE DISCARDABLEBEGINIDS_APP_TITLE "resource_file"IDS_HELLO "Hello World!"IDC_RESOURCE_FILE "RESOURCE_FILE"END#endif // English (U.S.) resources///////////////////////////////////////////////////////////////////#ifndef APSTUDIO_INVOKED/////////////////////////////////////////////////////////////////////// Generated from the TEXTINCLUDE 3 resource./////////////////////////////////////////////////////////////////////#endif // not APSTUDIO_INVOKED
Porting Icons and Cursors
Icons
Here‘s the fragment of the resource file that describes the application‘s icons:
// Icon with lowest ID value placed first to ensure application// icon remains consistent on all systems.IDI_RESOURCE_FILE ICON DISCARDABLE "resource_file.ICO"IDI_SMALL ICON DISCARDABLE "SMALL.ICO"
The first icon shown, IDI_RESOURCE_FILE, is the application‘s main icon. That is, it is the one that is displayed when the user creates a shortcut to the application. Win 32 applications‘ main icons are 32 by 32 pixels and may use the full range of system colors. By contrast, Windows CE icons must be no larger than 16 by 16 pixels, and must be explicitly loaded by the application. If the icon is too large, it won‘t load. The second icon in the resource file, IDI_SMALL, is used to represent the application in list views ( Windows Explorer, for example). This icon is superfluous on the CE side and should be eliminated.
Task 1:
Modify the application‘s main icon so that its dimensions are exactly 16x16 pixels. Since many CE devices are limited in their color depth, check to make sure your icon still "reads" correctly in black and white. ( Most paint programs have a function that let you change a color image to grey scale or black and white. ) Delete the small icon‘s file and references from the project.
Cursors
The resource_file application includes no special cursor resources. However, this simply means that the app doesn‘t store any images specifically intended to be displayed as cursors in it‘s resource file. Win 32 applications would typically use the APIs LoadCursor, LoadCursorFromFile, or CreateCursor to get or create a special cursor.
In most cases, using special purpose cursors is a bad practice under Win CE, both because it is unnecessarily wasteful of memory and because the concept of a cursor is permeated by the assumption that the user has a keyboard and a mouse as well. By contrast, Windows CE devices are heavily biased toward stylus input. Since the user taps the screen to choose the "current position" (which is really just the location of the input focus) a cursor is superfluous. Most Windows CE devices provide implicit support for the wait cursor, however. Standard practice is to display the wait cursor whenever you undertake an operation that will make the system unresponsive for a noticeable period of time. Here‘s how to load the native wait cursor:
//LoadCursor loads the cursor image and returns the handle//SetCursor sets the new cursor and returns the handle to//the old cursorhOldCursor = SetCursor(LoadCursor( NULL, IDC_WAIT));Here‘s one more thing of note about custom cursor resources. The file that encodes a cursor resource includes information for a variety of display devices. On the desktop, this means the cursor is drawn correctly for all resolution and aspect ratios, so the actual cursor file may be several times as large as the bitmap used for the cursor. Also, Windows CE doesn‘t support color cursors, and attempting to load one may produce catastrophic results.
Task 2:
Identify code that manipulates the cursor. Except for code that displays the wait cursor during lengthy operations, eliminate calls to cursor handling functions. Where the cursor changes signal change of mode or application state, devise ways to inform users. For example, use message boxes or modify the application‘s caption on the taskbar icon
Accelerators
Here are the lines from the resource file that encode the default accelerator for the resource_file application:
IDC_RESOURCE_FILE ACCELERATORS MOVEABLE PUREBEGIN"?", IDM_ABOUT, ASCII, ALTENDThis entry means a user can type Alt+? To open the application‘s About box . Much of what was said about cursors applies to accelerators. They are not as wasteful of memory or as risky if misused, but they lose their meaning in an environment where the user has options such as the stylus, voice input and rich ink.
Task 3:
Eliminate code that defines or manipulates accelerators.
Strings
Here‘s the application‘s string table resource code:
STRINGTABLE DISCARDABLEBEGINIDS_APP_TITLE "resource_file"IDS_HELLO "Hello World!"IDC_RESOURCE_FILE "RESOURCE_FILE"ENDSo far, we‘ve mostly been tossing unnecessary things out. Not so with the string table, which is a key tool for several reasons. First, Windows CE has aggressively embraced the world market for handheld and palmtop computers. This is the motivation behind CE‘s standardization on Unicode. Putting all the application‘s static text in the string table means that an application can be fully translated by manipulating only the resource file and the help files. It doesn‘t even have to be recompiled, only linked with a new language version of the resource file. More important, however, is the string table‘s potential to conserve the runtime memory. In a nutshell, here‘s the memory advantage of using the stringtable.
Every Windows CE application occupies a region of read only memory in which the program image and read only static data are stored. This region is separate and distinct from the read/write memory used by an executing application. String resources are stored in read only memory, never used en masse, but instead loaded on demand at runtime by an application. This dramatically reduces the application‘s footprint in the memory region used for the application heap and stack.
Task 4:
Locate the literal text in your application and move it to the string table. Replace the literals with calls to LoadString.
Menus
Here is the menu section for the resource_file application:
// Menu//IDC_RESOURCE_FILE MENU DISCARDABLEBEGINPOPUP "&File"BEGINMENUITEM "Open", ID_FILE_OPENMENUITEM "Close", ID_FILE_CLOSEMENUITEM "Save", ID_FILE_SAVEMENUITEM "SaveAs", ID_FILE_SAVEASMENUITEM "Print", ID_FILE_PRINTMENUITEM "Print Setup", ID_FILE_PRINTSETUPMENUITEM SEPARATORMENUITEM "E&xit", IDM_EXITMENUITEM SEPARATORMENUITEM "Recent Files", ID_FILE_RECENTFILESENDPOPUP "More Menus"BEGINPOPUP "Submenu 1"BEGINMENUITEM "Item1", ID_SUBMENU1_ITEM1MENUITEM "Item2", ID_SUBMENU1_ITEM2ENDENDPOPUP "&Help"BEGINMENUITEM "&About ...", IDM_ABOUTENDENDWith menus, we come to a serious porting effort. First, we‘ll look at what we can get rid of and then we‘ll examine the changed nature of menu handling under Windows CE.
In the File menu, you can eliminate Exit because it duplicates a standard element on the Windows CE menu bar. You can also eliminate File.Print Setup. Printing is supported on CE devices only thru the serial or infrared ports, which essentially amounts to transferring a file to a desktop computer and delegating to its printer, so you can‘t effect changes in printer setup from the handheld. For this reason you may want to eliminate File.Print, since it‘s basically a subterfuge for moving a file to the desktop and doing the file handling there.
Skipping over the "More Menus" popup for now, look at the "Help" menu. It‘s only purpose in this case is to display an item that shows the application‘s about box. "Help" should go because it is a waste of valuable screen real estate. The About box. Should go because it‘s a waste of valuable memory resources. If you feel strongly about displaying copyright and version info, use MessageBox when the program opens to do this. The dialog template and code that implants the MessageBox function is already present, so using it to display a splash screen with copyright, owner and version number information incurs very little overhead.
Now let‘s look more closely at the "More Menus" item. This is our first trela instance of making a choice to keep or eliminate a feature based on its congruence with the spirit of the Windows CE environment. "More Menus" resource description is shown below:
POPUP "More Menus"BEGINPOPUP "Submenu 1"BEGINMENUITEM "Item1", ID_SUBMENU1_ITEM1MENUITEM "Item2", ID_SUBMENU1_ITEM2ENDENDFloating menus and popup submenus are a key thing for which to be on the lookout when deciding whether to move existing menu code or to undertake redesign. If you have nested POPUP statements in the menu portion of the resource file, you need to consider changing the menu design. More that one level of nesting ( e.g. a POPUP submenu that include another POPUP) mandates. POPUPS on the desktop are used to help organize groups of relationships among menu items. On a CE device, scarcity of memory and screen real estate make this approach cumbersome and wasteful.Windows CE Menu Bars
From a realistic point of view, users aren‘t likely to be doing (or attempting to do) a very large variety of things at one time on a CE device. Because of their small screen size and relatively slow processing speed, it‘s reasonable to assume task-oriented patterns of user behavior. Probably for this reason, as well as the physical limits of the device, the designers of Windows CE have emphasized a more flexible, dynamic method of displaying menus than the one on which we mostly relied on the desktop. CE‘s use of menu bars provides a highly efficient way of displaying compact menus that are tailored to the task in which the user is engaged.
Instead of creating one menu for an application that presents every possible action or mode, you dynamically create and display a variety of task specific menu bars. When the user signals a change in task activity, you destroy the old menu bar and create a new one that supports the require task.Next, lets take a look at an application that demonstrates the versatility of menu bars. Before we do so, I‘ll say a word about the source files of the example apps. I use Visual C++ to build and test my Windows CE applications, both for the purposes of this book and in my business. I have found that in order to get things to build and link smoothly (most particularly, in order to avoid problems with unresolved function references at link time ), I start new projects using the following procedure:

Figure 2 - Create a Windows CE Project
Open the dialog box shown above from the File.New.Project Menu. Choose WCE Application from the left pane, and check every processor you wish to target in the Platforms pane. This last bit is important, because it‘s difficult to add platforms later.

Figure 3 - Choosing Initial Project Files
Choose "A typical "Hello World" application from the list in this dialog box. Though it‘s tempting to start with an empty project and import some of your own files into a clean space, it hasn‘t worked well for me. Using the "Hello World" files sets up all the linkage parameters and is much faster and less error prone.

Figure 4 - Files and target platforms for the new project
Build and run the Hello World app before you go on. In particular, check it‘s list of target platforms to make sure you got all of the ones you intended. There is a pair of toolbar dropdowns that you can check if you use Visual C++. The first is The WCE Configuration bar, which shows the available Windows CE configurations. It should include entries for the Handheld PC and the Palmtop PC, plus the version numbers of the SDK for each. Another control, the Build Toolbar, list all of the targets for which you can generate code in this project. The list should include all the processor types you specified in the Platforms pane of the New Project window, with Release and Debug build versions for each.
Looking Ahead
In the next installment, we‘ll fully explore the Windows CE menu bar. You‘ll learn how to build menu bars and insert controls. Since you‘ll probably be leaving at least some of your desktop application‘s menu functionality behind, we‘ll introduce alternative interface techniques which are both in the spirit of CE and in keeping with the limited screen size of typical CE devices. In particular, we‘ll focus on how to build, display and manage command bars, a key Windows CE interface construct.
About the Author
Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, entry time data validation.
In addition to writing for Developer.com, she has written several books includingMaking Win 32 Applications Mobile. She has also written numerous articles on programming technology for national publications including Dr. Dobbs, BYTE Magazine, Microsoft Systems Journal, PC Magazine; Computer Shopper, Windows Sources and Databased Advisor.