一文講清楚Windows Mobile和Wince(Windows Embedded CE)的字元集問題

來源:互聯網
上載者:User
文章目錄
  • 情況1
  •  
  • 情況2
  • 情況3
  • 情況4
背景

開發過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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.