近来两周我们花了大部分时间将已有的运用圭表移植到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学问。1. 不要在仿真器上运用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法度模范时应用的包蕴文件和库文件。2. 不要杂沓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*/3. 不要将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,4399口袋精灵2电信新区喜迎圣诞六大疏通齐出_SIZE为双数,你就不妨防止问题的形成。可是,在某些状态下你恐怕不能这么做。比方,借使标准是从一台式PC输入数据,你恐怕不得不采纳事先界说过的二进制格式,纵然它对H/PC不适合。在这种状态下,你必须采纳函数,这些函数用字符指针抑制字符串而不是TCHAR指针。借使你知道字符串的长度,就不妨用memcpy()复制字符串。是以,采纳逐个字节解析Unicode字符串的函数恐怕足以确定字符串在widechars中的长度。4. 在ANSI和Unicode字符串之间进行翻译 借使你的Windows CE行使标准接口于台式PC,恐怕你必须操纵PC机中的ANSI字符串数据(比方,char字符串)。纵然你在标准中只用到Unicode字符串,这都是究竟。 你不能在Windows CE上解决一个ANSI字符串,由于没有操纵它们的库函数。最佳的解决方法是将ANSI字符串调换成Unicode字符串用到H/PC上,然后再将Unicode字符串调换回ANSI字符串用到PC上。为了告终这些调换,可采纳MultiByteToWideChar()和WideCharToMultiByte () Win32 API 函数。5. 对于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 functionsMulti-Byte (ANSI) to WideChar (Unicode)atow() converts from ANSI to widecharwtoa() 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 on 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 returnedmust 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 returnedMust 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()函数更难以限制标的目的指针所指向的字符串的长度。6. 选取正确的字符串比较函数 要是你要分类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()函数来分辩大小写7. 不要运用相对路径 与Windows NT不一律,Windows CE没有目前目次这个概念,于是,任何路径但是相应付根目次而言的。假若你的软件给文件或目次运用相对路径,那么你很或许把它们移到其余场所了。譬喻,路径".\abc"在Windows CE中被当作"\abc"应付。8.移走了对calloc()和 time()函数的挪用 C运行库中的calloc()函数不能运用,但是malloc()函数能够代庖calloc()函数。而且不要忘记,calloc()函数初始化时分配的内存为零,而malloc()函数不一律。同样,time()函数也不能运用,但你能够运用Win32函数GetSystemTime()函数代庖time()函数。 始末以上的告诫后,你会快乐地学习结果令你赞叹的两点规戒。9. 不必要改造Win32 输入/输出(I/O)文件的挪用 Win32的输入输出函数,Windows CE也赞成。允许你象访谒Win32文件联众世界下载安装编制那样访谒器械。CreateFile()函数在Windows CE中不能辩认信号FILE_FLAG_RANDOM_ACCESS,但是这个信号仅用作可选的磁盘访谒,而且不影响函数挪用的功用。10. 不要担心字节的处境 当我们把应用轨范写入Windows CE时,有了一个美好的察觉,那就是Windows CE的数字数据类别的字节处境与Intel组织的字节处境一样,在整个的处分器上,Windows CE均支持。 几乎象整个的数据库引擎一样,Raima数据库约束器在数据库文件中以二进制格式保存数字数据。这就意味一个记录非论何时写入数据库或从数据库读出,均被当作一系列的字节来处分,不管它域的内容。只要数据库文件不要传给其它任何系统,数字数据的字节处境问题就处分了。倘若数据库文件被一个来自原始系统且带有不同字节处境的处分器访问,数字数据将被误解。 非论何时,当你在拥有不同处分器的死板上传输文件时,就会展现这个问题。在这个问题上,值得快乐的是整个类别的处分器都应用无别的字节处境。 在应用Windows CE时,这10点规戒应该引起你充足的着重,防止学习时走弯路。
(本文地址:http://www.huatend.com/doudizhuxiaoyouxi/20120129/1911.html) |