比来两周我们花了大部门时间将已有的应用圭臬移植到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_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.592zt.com/youxixiazai/20100829/479.html) |