A!die Software Studio Welcome to A!Die Software Studio

Window Kits 10 的 BUG 导致 std::cout 不能输出中文 (Visual C++ 2015)

by adie
2018-04-26 11:29:09

在使用中发现, 一旦设置了 setlocale(LC_ALL, "chs"); 之后, std::cout 就不能在控制台输出中文了. 只有设置 setlocale(LC_ALL, "C"); 才能正确输出中文.

用 std::cout.imbue(std::locale::locale("C")); 单独设置 std::cout 的 locale 也无效.

跟踪发现, std::cout 最终会调用 fputc, 对中文, locale 为 C 时 fputc 返回输出的字符串, locale 为 chs 时 fputc 返回 EOF.

继续跟踪 fputc 发现, 对不同的 locale, write_requires_double_translation_nolock 会返回不同的值, 从而走向不同的分支.

在 write_requires_double_translation_nolock 中通过获取线程的 local 是否为 C 来做了一个判断, 源代码如下:

 

write.cpp:87~92

2
3
4
5
6
    // Get the current locale.  If we're in the C locale and the file is open
    // in ANSI mode, we don't need double translation:
    __acrt_ptd* const ptd = __acrt_getptd();
    bool const is_c_locale = ptd->_locale_info->locale_name[LC_CTYPE] == nullptr;
    if (is_c_locale && _textmode(fh) == __crt_lowio_text_mode::ansi)
        return false;

对于 locale 不是 C 的, 输出会通过这个判断转向 write_double_translated_ansi_nolock 函数来执行, 而在这个函数执行时将会在文件第 153 行的 if (mbtowc(&wc, source_it, 2) == -1) 处返回 -1 参数错误. 原因在于 source_it 的内存中只包含了一个字符, 而传入 mbtowc 函数时却使用了 2 作为内存的大小.

通过对比不同版本的 SDK, 发现在 C:\Program Files (x86)\Windows Kits\10\Source\10.0.10150.0\ucrt\lowio\write.cpp 和 C:\Program Files (x86)\Windows Kits\10\Source\10.0.10240.0\ucrt\lowio\write.cpp 中的第 150 行 if (source_it < buffer_end) 在新版本的 SDK  C:\Program Files (x86)\Windows Kits\10\Source\10.0.16299.0\ucrt\lowio\write.cpp 中已经被修改为 if ((source_it + 1) < buffer_end) 了. 这应该就是为了修正这个 BUG 的.

 

在 VC2015 中将项目属性的 目标平台版本设置 改为 10.0.16299.0, 编译产生错误:

LINK : fatal error LNK1158: 无法运行“rc.exe”

查看运行目录发现,  VC2015 为该 SDK 添加的执行目录为 C:\Program Files (x86)\Windows Kits\10\bin\x86, 而此 SDK 存放编译程序的目录实际为 C:\Program Files (x86)\Windows Kits\10\bin\10.0.16299.0\x86, 于是手动添加该目录后重新编译通过.

使用新版本的 SDK 后该 BUG 解决, std::cout 可以正确输出中文了.

 

▲评论

X 正在回复:
姓 名: 留下更多信息
性 别:
邮 件:
主 页:
Q Q:
来 自:
职 业:
评 论:
验 证:


Valid HTML 4.01 Strict Valid CSS!
Copyleft.A!die Software Studio.ADSS
Power by webmaster@adintr.com