背景

开发过Windows Mobile和Wince(Windows Embedded CE)的开发者,特别是Native C++开发者,或多或少都遇到过ANSI字符集和Unicode字符集的转换问题。本文试图把Windows Mobile和Wince(Windows Embedded CE)开发的字符集问题讲明白,其实这个题目有点ambitious和aggressive,就当成标题党吧。

简介

本文试图通过一篇文章讲清楚Windows Mobile和Wince(Windows Embedded CE) Native C++开发中字符集的转换问题。从字符集的概念入手,讲述Wince支持的所有字符串类型,以及各种类型的转换方法,最后给出使用建议。

什么是字符集

字符集(Character Set)是映射关系,其定义了字符和编码的关系。这里编码通常来说是1和0的bit。当前流行的计算机系统任何数据存储最终都表达为1和0。而这些1和0在不同字符集下映射成不同含义的字符。

计算机发明和使用初期,存储设备都十分的昂贵,科学家们想尽办法来节省成本,因此开始的是,最常见的字符集是单字节字符集(Signle-byte),所谓单字节字符集就是使用一个byte来代表一个字符,单字符集的典型是ASCII (American Standard Code for Information Interchange),你看这不是国籍标准,仅仅是美国标准,压根就没有考虑咱们感受,咱们从甲骨文开始发展汉字,美国人看到就A~Z几个字母。

这个ASCII 表,学过C语言的人都学过,以前考试也用到,需要背下来的。

ANSI(American National Standards Institute) 是在ASCII 7bit 编码标准 (ASA X3.4-1963)的基础上,加入了欧洲字母发展出来的一个标准。

但是单字节字符集有个最要命的缺点是一个byte只要8个bit,也就是最多表示256 (28)个可见和不可见的字符。 对于英语国家可能是够用的,但是对于说中文的国家,汉字是没办法通过256个字符表达的。因此慢慢出来国际标准的字符集Unicode。

Wince与Unicode

对于刚刚接触Windows Mobile和Wince(Windows Embedded CE) Native C++开发的人来说,会有这样的想法:Windows Mobile和Wince仅仅支持Unicode,不支持ANSI的。TinyXML是使用ANSI string的,但是Wince使用Unicode,那么TinyXML不能使用在Wince和Windows Mobile中。等等…… 其实这些想法有些错误,Wince是一个Unicode系统,没错,这表示Wince里面所有字符串处理代码都是基于Unicode编码,但是不表示Wince不支持ANSI。我们同样可以继续在Wince中使用ANSI,例如使用std::string,
char[]等。

但是为什么会有这些误区呢,先看一下下面的编译错误。

error C2664: 'wprintf' : cannot convert parameter 1 from 'const char [21]' to 'const
wchar_t *'

error C2664: 'DeleteFileW' : cannot convert parameter 1 from 'const char [21]' to 'LPCWSTR'

我敢保证刚刚接触Windows Mobile和Wince(Windows Embedded CE) Native C++开发的人10个有9个甚至10个都碰到过上述问题。在调用Win32 API的时候,使用MFC,WTL的接口的时候都会碰到这样的问题,因为我们习惯使用char*,std::string,但是恰恰Win32 API,MFC和WTL的函数入口中的字符串为Unicode,因此发生上述的编译错误。不知道为什么大家碰到这个错误后会形成一个错误的想法:Wince只是支持Unicode,不支持ANSI了。其实Wince还是支持ANSI的,我们定义单字符的char数组,甚至可以通过C
Runtime在Console中打印出ANSI string。

char ansiStr[] = "I am ANSI string";

printf(ansiStr);

Wince支持的字符串

既然Wince支持ANSI和Unicode,那什么时候用ANSI,什么时候用Unicode,下面我从在Wince开发中常常用到字符串讲起,然后讲述字符串的转换以及使用建议。

char*

char*和char[]没有本质的区别,都是指向内存的指针。ANSI版本的Win32的API中的字符串都是使用char*。 由于Win32的API是语言无关的,因此这些参数其实传递的是一段应该存放字符串的内存的指针。(很拗口,但是确实这样,呵呵)。在ANSI环境下使用纯粹C开发,程序是离不开char*的。

wchar_t *

LPWSTR, PWSTR等宏定义其实都是wchar_t*的定义, 最常见的LPCWSTR是const wchar_t*的宏定义。wchar_t表示16位Unicode字符。wchar_t*和wchar_t[]用来定义Unicode字符串。在Unicode版本下,所有Win32的API的字符串都由char*变成了wchar_t*了。

这个可以看一下头文件的预编译。例如以winbase.h的DeleteFile API为例。

WINBASEAPI

BOOL

WINAPI

DeleteFileA(

LPCSTR lpFileName

);

WINBASEAPI

BOOL

WINAPI

DeleteFileW(

LPCWSTR lpFileName

);

#ifdef UNICODE

#define DeleteFile DeleteFileW

#else

#define DeleteFile DeleteFileA

#endif // !UNICODE

在ANSI版本DeleteFile为DeleteFileA,参数的字符串定义为LPCSTR,也就是const char*,而Unicode版本的DeleteFile为DeleteFileW,参数字符串定义变成了LPCWSTR,也就是const wchar_t*。

决定DeleteFile到底是DeleteFileA或者DeleteFileW是由预编译宏 UNICODE 来决定的。

这个宏可以在项目属性里面配置,如下图:

当选择Use Unicode Character Set时候,预编译会增加宏UNICODE和_UNICODE。

但是需要注意的是,如果目标平台为Windows Mobile或者Wince,不管是否选择Use Unicode Character Set,UNICODE和_UNICODE的预编译都会加上,也就是说Wince下所有Win32 API都是Unicode版本的。

CString

CString最初在MFC里面封装,ATL和WTL分别封装了CString,这三个不同封装的CString具有语义兼容性,也就是他们提供的接口是相同的。使用CString的好处是可以同时兼容ANSI和Unicode,如下例子:

CString str = "Independent String";

m_wndPic.SetWindowText(str);

m_wndPic是一个CStatic控件,上面的代码不管在ANSI或者在Unicode都能使用,无需更改。

下面以ATL CString为例子讲述CString是如何同时支持支持ANSI或者Unicode的。

typedef CAtlString CString;typedef CStringT< TCHAR, StrTraitATL< TCHAR > > CAtlString;

CString存储字符串的类型由TCHAR来决定的,而TCHAR又是由UNICODE预编译来决定,见下面的宏定义。

#ifdef UNICODE // r_winnt

typedef WCHAR TCHAR, *PTCHAR;

#else /* UNICODE */ // r_winnt

typedef char TCHAR, *PTCHAR;

#endif /* UNICODE */

以此CString使用字符串类型根据预编译选项来自动决定。

std::string

STL里面的string,封装的是单字节字符,由于其跨平台的特性,我编写的代码中大量使用std::string,其实准确来说我大量使用STL。例如我一般使用std::string来操作TinyXML。抛开Wince平台不说,使用std::string基本上没有缺点,可以跨任何支持标准C++的平台。可是在Wince和Windows Mobile下做开发,情况有点不一样,因为std::string封装的是单字节字符,所以如果需要调用Win32的API,使用MFC,ATL和WTL的功能时都需要转型,这姑且算是一个缺点吧,但是熟悉了转换以后,使用std::string一点问题都没有。

std::wstring

STL里面的string的Unicode版本,和std::string一样,使用了unicode字符进行封装。其实std::wstring我用的不多,用std::string已经够了。

如何转换Wince支持的字符串

既然Windows Mobile和Wince(Windows Embedded CE)支持上述的字符串,那么我们开发的时候会碰到这些字符串直接相互转换的问题,下面通过例子演示如何转换。

转换过程我推荐使用ATL的宏,关于ATL的宏可以参考  ATL
and MFC String Conversion Macros

这些宏的命名规范为

CSourceType2[C]DestinationType[EX]

SourceType/DestinationType

Description

A

ANSI character string.

W

Unicode character string.

T

Generic character string (equivalent to W when _UNICODE is defined, equivalent to A otherwise).

OLE

OLE character string (equivalent to W).

A表示ANSI string,W表示Unicode string,T表示通用string,根据预编译来决定类型。OLE和W一样,我从来不用OLE。

例如CT2CA就是通用string转成ANSI string。

转换到char*

void ConvertToCharArray()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



CString cstr("ATL CString");



std::string stlStr("STL string");

std::wstring stlWStr(_T("STL wstring")); //use _T() convert const string to wchar_t string



printf("All string convert to char*\n");

strcpy(ansiStr, CT2CA(unicodeStr));

printf("Convert from wchar_t*, %s\n", ansiStr);



strcpy(ansiStr, CT2CA(cstr));

printf("Convert from CString, %s\n", ansiStr);



strcpy(ansiStr, stlStr.c_str());

printf("Convert from std::string, %s\n", ansiStr);



strcpy(ansiStr, CT2CA(stlWStr.c_str()));

printf("Convert from std::wstring, %s\n", ansiStr);

}

例子中用到了ATL CString,如果新建的是Win32项目需要加入ATL支持,方法可以参考: 在Windows
Mobile和Wince(Windows Embedded CE)下Win32项目加入ATL支持

上面讲过ATL CString和WTL以及MFC CString语义相同,因此本文所有CString的代码在MFC下同样有效。

转换到wchar_t*

void ConvertToWCharArray()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



CString cstr("ATL CString");



std::string stlStr("STL string");

std::wstring stlWStr(_T("STL wstring")); //use _T() convert const string to wchar_t string



printf("All string convert to wchar_t*\n");

wcscpy(unicodeStr, CComBSTR(ansiStr));

wprintf(_T("Convert from char*, %s\n"), unicodeStr);



wcscpy(unicodeStr, cstr);

wprintf(_T("Convert from CString, %s\n"), unicodeStr);



wcscpy(unicodeStr, CComBSTR(stlStr.c_str()));

wprintf(_T("Convert from std::string, %s\n"), unicodeStr);



wcscpy(unicodeStr, stlWStr.c_str());

wprintf(_T("Convert from std::wstring, %s\n"), unicodeStr);

}

这里使用了微软推荐的CComBSTR(),而不是CA2W()。

转换到CString

void ConvertToCString()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



CString cstr("ATL CString");



std::string stlStr("STL string");

std::wstring stlWStr(_T("STL wstring")); //use _T() convert const string to wchar_t string



printf("All string convert to CString\n");

cstr = ansiStr;

wprintf(_T("Convert from char*, %s\n"), cstr);



cstr = unicodeStr;

wprintf(_T("Convert from wchar_t*, %s\n"), cstr);



cstr = stlStr.c_str();

wprintf(_T("Convert from std::string, %s\n"), cstr);



cstr = stlWStr.c_str();

wprintf(_T("Convert from std::wstring, %s\n"), cstr);

}

转换到std::string

void ConvertToStlString()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



CString cstr("ATL CString");



std::string stlStr("STL string");

std::wstring stlWStr(_T("STL wstring")); //use _T() convert const string to wchar_t string



printf("All string convert to STL string\n");

stlStr = ansiStr;

printf("Convert from char*, %s\n", stlStr.c_str());



stlStr = CT2CA(unicodeStr);

printf("Convert from wchar_t*, %s\n", stlStr.c_str());



stlStr = CT2CA(cstr);

printf("Convert from CString, %s\n", stlStr.c_str());



stlStr = CT2CA(stlWStr.c_str());

printf("Convert from std::wstring, %s\n", stlStr.c_str());

}

转换到std::wstring

void ConvertToStlWstring()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



CString cstr("ATL CString");



std::string stlStr("STL string");

std::wstring stlWStr(_T("STL wstring")); //use _T() convert const string to wchar_t string



printf("All string convert to STL wstring\n");

stlWStr = CComBSTR(ansiStr);

wprintf(_T("Convert from char*, %s\n"), stlWStr.c_str());



stlWStr = unicodeStr;

wprintf(_T("Convert from wchar_t*, %s\n"), stlWStr.c_str());



stlWStr = cstr;

wprintf(_T("Convert from CString, %s\n"), stlWStr.c_str());



stlWStr = CComBSTR(stlStr.c_str());

wprintf(_T("Convert from std::string, %s\n"), stlWStr.c_str());

}

纯C Runtime库转换

有时候使用Win32进行纯C的开发,例如进行今日插件的开发,不使用ATL,WTL,MFC以及STL的情况下,也会有转换char*和wchar_t*的需求,但是不能使用ATL的宏,下面演示如何使用C Runtime库来转换。

void ConvertToWCharArrayUsingCRuntime()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



printf("Convert to char* from wchar_t* using C Runtime library.\n");

sprintf(ansiStr, "%S", unicodeStr);

printf("Convert from wchar_t*, %s\n", ansiStr);

}



void ConvertToCharArrayUsingCRuntime()

{

char ansiStr[255] = "ANSI string";

wchar_t unicodeStr[255] = _T("Unicode string"); //use _T() convert const string to
wchar_t string



printf("Convert to wchar_t* from char* using C Runtime library.\n");

swprintf(unicodeStr, _T("%S"), ansiStr);

wprintf(_T("Convert from char*, %s\n"), unicodeStr);

}

使用建议

上面讲述了Windows Mobile和Wince(Windows Embedded CE)支持那么多字符串,那么我们到底如何选择使用的字符串呢?其实这个没有准则,我下面谈一下我的经验。这不是准则,所以只做参考之用。

一.尽量避免使用char*和wchar_t*

除了以下情况,不得不使用char*和wchar_t*时,大部分时候尽量避免使用char*和wchar_t*。

情况1

做今日组件开发,只是使用Win32,如果不依赖于ATL,WTL,MFC和STL,那么没得选择只能使用char*和wchar_t*。

关于今日组件的开发,可以参考:

关于在Windows
Mobile下今日插件使用WTL的问题

情况2

封装DLL或者通用静态库提供给第三方使用,例如TinyXML, CppUnitLite这样的类库,他们内部都在char*基础上实现字符串处理类,这样库就不依赖于ATL,WTL,MFC和STL了。

关于TinyXML, CppUnitLite可以参考:

Windows
Mobile和Wince下使用TinyXML进行Native C++的开发

Wince和Windows
Mobile下native C++的单元测试

Windows
Mobile下使用CppUnitLite输出测试结果

情况3

封装DLL给.NET Compact Framework使用,接口函数只能使用char*和wchar_t*,不能使用CString和std::string。

关于DLL的封装,可以参考:

Windows
Mobile和Wince(Windows Embedded CE)下如何封装Native DLL提供给.NET Compact Framework进行调用

Windows
Mobile和Wince(Windows Embedded CE)下封装Native DLL进一步探讨

情况4

可以使用char*和wchar_t*包括一些字符串常量,用于替换宏定义。

除了上述情况以外,应当尽量避免使用char*和wchar_t*,而是用CString,std::string等封装好的字符串类。

二.程序需要同时支持PC和Window Mobile版本时使用CString

如果使用C++加上ATL,WTL或者MFC开发,程序需要同时支持Windows桌面版和Windows Mobile以及Wince,可以考虑使用CString。CString可以很好的兼容ANSI和Unicode版本。

例如我封装的一个SQL Server Compact的数据库访问类,使用到CString,这个类可以支持PC和Windows Mobile。可以参考:

Windows
Mobile下Native C++访问SqlCe的封装

三.程序需要跨平台时使用std::string

程序不仅仅用于windows平台,而且用于Linux,Unix,BSD等平台,可以考虑使用std::string,我一般不使用std::wstring,觉得没有这个必要,使用std::string在需要的时候转换就可以了。但是如果追求更高的跨平台性,那只能使用char*和wchar_t*了,连STL都不依赖。

我个人喜欢使用std::string,因为我大量使用STL。在设计的时候把界面和处理逻辑分开,处理逻辑内部统一使用std::string以及STL的容器。需要界面交互出来,或者调用Win32的时候进行字符串的转换。

可以进一步参考的文章

http://www.tenouk.com/ModuleG.html

http://www.codeproject.com/KB/string/cppstringguide1.aspx


Windows Mobile和Wince(Windows Embedded CE)的字符集问题的更多相关文章

  1. (转) 关于Windows CE和Windows Mobile

    转发自http://www.cnblogs.com/chump/articles/1281955.aspx 一.Windows CE Windows CE是微软的嵌入式操作系统主要的一种,面世于199 ...

  2. Windows Mobile 6开发环境搭建

    Windows Mobile 6开发环境搭建 本文主要介绍在已有的Visual Studio 2005和Microsoft SQL Server2008环境基础上,如何搭建Windows Mobile ...

  3. 使用Visual Studio 2008创建你的第一个Windows Mobile程序介绍

    使用Visual Studio 2008创建你的第一个Windows Mobile程序介绍 Windows MobileMobileWindowsMicrosoftWinForm 介绍 Microso ...

  4. Windows Embedded CE 6.0开发环境的搭建

    最近开始在学习嵌入式,在这里首先得安装Windows Embedded CE 6.0,其中遇到了很多问题,电脑的系统以及相关配置都会在安装过程中受到影响,因此笔者就安装中的问题以及环境搭建来介绍一下. ...

  5. (转) Windows Mobile和Windows CE的区别

    转发自 http://blog.sina.com.cn/s/blog_6250bbe60100tsf3.html WinCE Windows CE 是一个可定制的操作系统: Windows Mobil ...

  6. Windows Embedded CE 6.0开发环境的搭建(2)

    最近开始在学习嵌入式,在这里首先得安装Windows Embedded CE 6.0,其中遇到了很多问题,电脑的系统以及相关配置都会在安装过程中受到影响,因此笔者就安装中的问题以及环境搭建来介绍一下. ...

  7. Windows Embedded CE 6.0 下载地址和序列号

    Windows Embedded CE 6.0 下载地址和序列号 安装起来特麻烦 程序下载地址 http://download.microsoft.com/download/a/0/9/a09e587 ...

  8. Win10系统windows mobile设备中心无法连接WinCE采集器

    1.开始-->运行,输入services.msc回车 2.在打开的服务界面中,找到“基于Windows Mobile 2003的连接设备” 3.右击属性,修改成自动 4.点击登陆选项卡,选择本地 ...

  9. [WinCE] [Win10] Win10 Creator 升级后 Windows Mobile Device Center 不能打开

    运行 services.msc 找到 Windows Mobile 2003-based device connectivity服务,右键属性,Log On选项卡选择 Local System acc ...

随机推荐

  1. [Matlab+C/C++] 读写二进制文件

    introduction 因为Matlab操作简单.方便,它被应用于很多领域:音频处理,图像处理,数值计算等.尽管MATLAB容易操作,但受限于他的语言解释机制,MATLAB的执行速度通常较低.C/C ...

  2. 拦截器(Interceptor)中的invocation.invoke()

    关于在Struts2的自定义的验证拦截器(Interceptor)中的invocation.invoke()是什么意思? package com.xjtu.interceptor; import co ...

  3. Low-rank approximations

    Low-rank approximations Give matrix and a positive integer , we wish to find an matrix of rank at mo ...

  4. SQL Server 扩展事件(Extented Events)从入门到进阶(3)——通过界面操作Extented Event

    本文属于 SQL Server扩展事件(Extended Events)从入门到进阶 系列 对于接纳扩展事件,其中一个最大的障碍就是要对XML和XQuery有一定的了解以便分析数据.我们可以使用T-S ...

  5. [ExtJS5学习笔记]第三十四节 sencha extjs 5 grid表格之java后台导出excel

    继上次使用js前端导出excel之后,还有一个主要大家比较关注的是后台实现导出excel,因为本人开发使用的java所以这里使用apache的开源项目poi进行后台excel的导出. 本文目录 本文目 ...

  6. Linux for sougou ping yin (http://pinyin.sogou.com/linux/help.php)

    安装指南 Ubuntu / Ubuntu Kylin 14.04 LTS 版本 只需双击下载的 deb 软件包,即可直接安装搜狗输入法. Ubuntu 12.04 LTS 版本 由于 Ubuntu 1 ...

  7. 在Gazebo中使用DEM構建起伏地形環境

    所需資料下載地址: 1. https://bitbucket.org/osrf/gazebo_tutorials/raw/default/dem/files/ 数字高程模型(致謝谷歌翻譯)概述数字高程 ...

  8. [virtualenv]生产环境中使用virtualenv

    virtualenv 对于python开发和部署都是好工具,可以隔离多个python版本和第三方库的版本,这里作者总结了几个常用python服务怎么样结合virtual部署 原文链接 Python 中 ...

  9. XCode5添加新建类模板(Cocos2dx Template Class for Scene or Layer)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=505 因为常用cocos2dx开 ...

  10. UNIX网络编程——UDP回射服务器程序(初级版本)以及漏洞分析

    该函数提供的是一个迭代服务器,而不是像TCP服务器那样可以提供一个并发服务器.其中没有对fork的调用,因此单个服务器进程就得处理所有客户.一般来说,大多数TCP服务器是并发的,而大多数UDP服务器是 ...