使Win32应用程序在ANSI和UNICODE上运行

7

我希望我的Win32 C++应用程序能够在任何编码版本(UNICODE和ANSI)上运行。现在,我有点困惑两种(或更多?)编码之间的确切区别是什么?

要使我的Win32应用程序跨编码兼容,这是否意味着我必须检查我的代码,并将每个std::string替换为std::wstring,然后将每个char替换为wchar_t*,然后将每个文字字符串("")替换为L""?

如果我的应用程序在一个UNICODE机器上运行,而我的应用程序中有一个std::string会发生什么?

您有关于使我的应用程序跨编码兼容所需步骤的建议吗? 例如: - 将所有c_strings和strings更改为它们的UNICODE等效项 - 将任何Win32函数更改为uncide版本(例如,从getenv()更改为_wgetenv())


4
你是否真的在考虑支持Windows 9x? - Cheers and hth. - Alf
我在5年前做过,甚至我也没有费心使用ANSI。MSLU(Unicows.DLL)使得9x看起来足够像NT。 - MSalters
如果你在Win32上做任何不支持完整Unicode的事情,那么你要么做错了什么,要么处于支持过时操作系统的可怕境地...这就是Alf所说的。 - Jewel S
4个回答

7

如果我的应用程序在UNICODE机器上运行,并且我的应用程序中有一个std::string,会发生什么?

计算机本身不是ANSI或Unicode编码的,而是计算机操作系统所支持的。最后一个不支持Unicode的Windows版本是Windows 3.11 for Workgroups。如果您在Unicode上运行ASCII编译的应用程序。

这两种(或更多?)编码之间到底有什么区别?

什么是ASCII?
ASCII是一种7位编码技术,为美国英语中最常用的128个字符分配一个数字。这使得大多数计算机可以记录和显示基本文本。ASCII不包括其他国家经常使用的符号。

什么是Unicode?
ASCII的一个主要缺点是你只能拥有256个不同的字符。然而,像日语和阿拉伯语这样的语言有成千上万个字符。因此,ASCII在这些情况下无法使用。结果是Unicode,允许高达65,536个不同的字符。

Unicode是ISO和Unicode Consortium开发电子文本编码系统的一种尝试,其中包括所有现有的书写字母表。根据特定表示方式,Unicode使用8位、16位或32位字符,因此Unicode文档通常需要比ASCII或Latin-1文档多一倍的磁盘空间。Unicode的前256个字符与Latin-1完全相同。

在Win32中,通过#define定义UNICODE和_UNICODE宏来支持UNICODE。这反过来会使您的程序使用Win32函数的Unicode变体。

你有什么建议,我需要采取哪些步骤使我的应用程序跨编码兼容?

每个Win32函数(接受或返回字符串)都有两个变体,一个用于ASCII,一个用于Unicode。函数调用解析为其中之一,具体取决于是否定义了UNICODE宏。因此,您应该定义宏并开始使用函数的Unicode版本。例如:

将每个std::string替换为std::wstring,
将每个char替换为wchar_t*
将每个文字字符串("")替换为L""
利用Windows中的TCHAR支持等。

正如您指出的那样,这是您需要注意的事项列表,但请注意,这不是完整的列表。

基本上,您将必须在代码中使用所有类型和函数调用的Unicode版本。


2
Unicode 允许使用约 200 万个字符(2^21);它有多个“平面”,每个平面包含 65536 个字符。最常见的字符都在基本多语言平面中;这可能是混淆的来源。 - MSalters

3
当您为ANSI或Unicode编译程序时,会影响两个方面:
  1. 调用的API集。假设您的代码调用了CreateFile()函数。实际调用的API取决于您的编译器设置,可以是CreateFileA()CreateFileW()(ANSI或Wide(即Unicode))。内部NT内核对所有API使用Unicode。而ANSI API只是将其字符串参数转换为ANSI,并调用Unicode API。许多API仅支持Unicode。
  2. T*宏的扩展方式。TCHAR最终在ANSI模式中被扩展为char,在Unicode模式中被扩展为wchar_t
std::stringstd::wstring这样的内容在不需要调用API并向它们传递字符串之前不受影响。使用string还是wstring应该根据程序的需要来确定,而不是根据它是否以ANSI或Unicode编译。
您可以使用ATL轻松转换字符串。
// assume compiled for Unicode
#include <atlbase.h>

void myfunc() {
   USES_CONVERSION;

   std::string filename = "...";
   HANDLE hFile = CreateFile(A2W(filename.c_str()), ...

或者,如果您喜欢,可以使用A2T(),这样您的代码将在编译为ANSI或Unicode时都能工作。


3
最后一个没有内部使用Unicode的Windows版本是Windows ME。对于新代码的建议是只使用Unicode。当您需要读写以特定代码页编码的文件时,可能需要进行一些转换。
您的初始想法是正确的。如果您使用Microsoft的CString,则它有两个版本:CStringA和CStringW - 您需要更改一个编译器定义,它将在您指定CString的每个位置使用CStringW,并且一切都将正常工作。您应该使用std :: wstring而不是std :: string。在每个字符串文字前加上L""前缀或使用Microsoft的宏_T(""),它会转换为相同的东西。

1

在您的情况下,您可以使用TCHAR。

在UNICODE中,TCHAR是WCHAR。 在非UNICODE中,TCHAR是CHAR。

如果您想使用std::string,我建议您使用以下用法。

 #ifdef UNICODE
 #define std::tstring str::wstring
 #else
 #define std::tstring str::string
 #endif

而且,

在您的程序中使用std::tstring。


谢谢你的建议:),但是编译器定义是否与在编译时而不是在运行时做出这些决策有关,因此,如果我在我的ANSII计算机上编译并发布该.exe文件,如果我将其发送给俄罗斯的某个人,它仍然会失败? - user593747
4
“#define std::tstring std::wstring”并不是你想象中的那样。请注意,这个宏定义并不能达到你预期的效果。 - zwol
2
@user - 但如果你使用UNICODE定义编译它,它将在任何地方都能工作。为什么要费事做两个版本呢? - Bo Persson
1
我强烈建议不要这样做,应该在所有地方使用Unicode,而且应该使用(现代C ++)或typedef而不是#define来定义tstring。 - paulm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接