背景

开发过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. 设子数组A[0:k]和A[k+1:N-1]已排好序(0≤K≤N-1)。试设计一个合并这2个子数组为排好序的数组A[0:N-1]的算法。

    设子数组A[0:k]和A[k+1:N-1]已排好序(0≤K≤N-1).试设计一个合并这2个子数组为排好序的数组A[0:N-1]的算法.要求算法在最坏情况下所用的计算时间为O(N),只用到O(1)的辅助 ...

  2. Android自定义View(一、初体验自定义TextView)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51454685 本文出自:[openXu的博客] 目录: 继承View重写onDraw方法 自 ...

  3. Scikit-learn:模型选择Model selection

    http://blog.csdn.net/pipisorry/article/details/52250983 选择合适的estimator 通常机器学习最难的一部分是选择合适的estimator,不 ...

  4. 在windows和Linux上安装ImageMagick与jmagick,Maven配置、Java图片压缩代码(整理网上、结合自己情况、编写出来的新安装方式)

    安装过程(如图所示) .Exceptionin thread "main" java.lang.UnsatisfiedLinkError:C:\WINDOWS\system32\j ...

  5. JQuery 初探

    放暑假了,终于有时间能学点前端的东西了.JQuery就是我第一个选择,锋利的JQuery.这本书真的很好.下面以一个ToggleButton形式的小例子开场吧. 引入JQuery库 在网页上引用JQu ...

  6. Android 玩转IOC,Retfotit源码解析,教你徒手实现自定义的Retrofit框架

    CSDN:码小白 原文地址: http://blog.csdn.net/sk719887916/article/details/51957819 前言 Retrofit用法和介绍的文章实在是多的数不清 ...

  7. Android-满屏幕拖动的控件

    本文转载自师兄一篇博客:http://blog.csdn.net/yayun0516/article/details/52254818 觉得跟之前的模拟小火箭很相似,又有学习的地方,能作为知识补充.所 ...

  8. JDBC的使用五大步骤以及查询操作-数据库编程(二)

    jdbc的使用步骤 1.加载jdbc的驱动. 2.打开数据库的连接. 3.建立一个会话,然后执行增删改查等基本的操作. 4.对结果进行处理 5.对环境进行清理,比如关闭会话等. 查询操作 首先用Cla ...

  9. 剑指Offer——滴滴笔试题+知识点总结

    剑指Offer--滴滴笔试题+知识点总结 情景回顾 时间:2016.9.18 15:00-17:00 地点:山东省网络环境智能计算技术重点实验室 事件:滴滴笔试   总体来说,滴滴笔试内容体量不算多, ...

  10. Mac版Android Studio的安装和使用

    Android Studio已经出来很长时间了,据说谷歌会逐步放弃对Eclipse的支持,而把心思完全放在Android Studio上,鉴于Eclipse的各种不稳定,或许这将成一种趋势,因此,没事 ...