加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
更改了非Unicode语言,为何中文还乱码?
acmilan2016/08/13软件综合 IP:四川

本人喜欢使用英文系统,为了避免老软件乱码以及不能输入中文的问题,一般需要将系统区域设置中的“非Unicode程序的语言”设为简体中文,如图所示:

0.png

正确设置“非Unicode程序的语言”以后,基本上所有的中文程序都能正常运行了。

正因为如此,本人经常向本论坛、远景论坛、贴吧等的其他网友推荐英文版Windows,但是有些网友反映,有一小部分程序即使更改了也不行,这是怎么回事呢?

由于这是一种非常罕见的乱码,很长时间没有找到答案,直到我在使用VC++2008带的MFC时,发现它重现了这种乱码:

1.png

这种乱码是怎么造成的呢?我发现,乱码的地方都是用CStringA::LoadStringA从资源段字符串表加载的。于是,我在这个函数下了函数断点。发现LoadStringA有多个重载,其中一个重载如下:

Other
_Check_return_ BOOL LoadString( _In_ HINSTANCE hInstance, _In_ UINT nID ) { const ATLSTRINGRESOURCEIMAGE* pImage = AtlGetStringResourceImage( hInstance, nID ); if( pImage == NULL ) { return( FALSE ); } int nLength = StringTraits::GetBaseTypeLength( pImage->achString, pImage->nLength ); PXSTR pszBuffer = GetBuffer( nLength ); StringTraits::ConvertToBaseType( pszBuffer, nLength, pImage->achString, pImage->nLength ); ReleaseBufferSetLength( nLength ); return( TRUE ); }

发现它是由资源段直接读取Unicode字符串,再由ConvertToBaseType转换到ANSI字符串的,右击ConvertToBaseType转到定义,得到内容如下:

C++
static void ConvertToBaseType(_Out_cap_(nDestLength) _CharType* pszDest, _In_ int nDestLength, _In_count_(nSrcLength) const wchar_t* pszSrc, _In_ int nSrcLength = -1) throw() { // nLen is in XCHARs ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL); }

ConvertToBaseType就是包装了WideCharToMultiByte函数而已,其中_AtlGetConversionACP函数决定了转换的代码页,右击它继续查找定义,得到:

C++
inline UINT WINAPI _AtlGetConversionACP() throw() { #ifdef _CONVERSION_DONT_USE_THREAD_LOCALE return CP_ACP; #else return CP_THREAD_ACP; #endif }

会不会是CP_THREAD_ACP的问题呢?我查了一下,它跟随ThreadLocale而变化。我尝试在MFC初始化之前插入一个GetThreadLocale调用:

Other
// 唯一的一个 CLuanMaTest2App 对象 // 在theApp对象初始化前插入如下代码 class BeforeApp { public: BeforeApp() { CString str; str.Format("%x", GetThreadLocale()); MessageBox(NULL, str, "ThreadLocale", MB_OK); } }beforeApp; CLuanMaTest2App theApp;

6.png

至此真相大白——409是英文的LocaleID,804是简体中文的LocaleID。

稍后奉上更深度的分析以及解决方案

[修改于 8年11个月前 - 2016/08/14 18:44:34]

来自:计算机科学 / 软件综合
3
新版本公告
~~空空如也
acmilan 作者
8年11个月前 修改于 8年11个月前 IP:四川
824423

从上面的分析来看,CP_THREAD_ACP是罪魁祸首——它跟随ThreadLocale变化,而ThreadLocale跟随系统用户界面语言变化,导致了不匹配。

微软也给开发者留了一个解决办法,只要在stdafx.h最前面加上这样一句就行了:

C++
#define _CONVERSION_DONT_USE_THREAD_LOCALE 1

也可以将程序编译为使用Unicode字符集的程序,遇到需要多字节字符串的地方再手工转换,这样也可以避免这个问题。

至于微软为什么要挖这个坑,就不得而知了。就本人分析而言,这可能属于对Windows操作系统的复杂性考虑不足导致的误用现象。VC++2002最早出现这种现象。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
8年11个月前 修改于 8年10个月前 IP:四川
824424

经过更深入的实验,发现只要是控制台程序,ThreadLocale就跟随“非Unicode程序的语言”,而只要是GUI程序,ThreadLocale就跟随用户界面语言。下面的程序展示了实验方法。

Other
// LuanMaConsoleTest2.cpp : 定义控制台应用程序的入口点。 // #undef UNICODE #undef _UNICODE #include "stdafx.h" #include <windows.h> #include <atlbase.h> #include <atlstr.h> // 英文系统返回804 #pragma comment(linker, "/SUBSYSTEM:CONSOLE /ENTRY:mainCRTStartup") // 英文系统返回409 //#pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") int _tmain(int argc, _TCHAR* argv[]) { CString str; str.Format("%x", GetThreadLocale()); MessageBox(NULL, str, "ThreadLocale", MB_OK); return 0; } </atlstr.h></atlbase.h></windows.h>

至此,一个适用于最终用户的特殊乱码解决方法产生了。

适用于最终用户的解决方法

**第一个方案:**安装语言包。对不同用户帐户设置不同的显示语言,在需要运行这些程序的时候切换到使用简体中文的用户帐户,就不会乱码了。这也是最推荐的做法。

**第二个方案:**用十六进制编辑器修改exe。将exe改成控制台程序,ThreadLocale就不会跟随用户界面语言,而是跟随非Unicode程序语言,乱码问题也就解决了。如果使用的是Win7家庭版或专业版、Win8/8.1/10单语言版,就只能用这个方法了。

首先,下载一个十六进制编辑器,用它打开,找到"PE"这个字符串第一次出现的地方,光标放在字母P上面:

2.png

然后用编辑器的跳转功能,向前跳转5C(十六进制)字节,这是PE可执行文件的Subsystem字段的偏移(32位和64位程序通用)。

3.png

这个位置的原始值应该是02 00即GUI程序(如果不是就错了),将它改为03 00即控制台程序:

4.png

保存文件,重新运行,这时乱码已经不存在了。

5.png

美中不足是运行时有个黑色的控制台窗口,不过使用时可以将它最小化,不影响正常使用。

**第三个方案:**无意中发现的,安装IE11也可以解决这个问题。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
8年11个月前 修改于 8年11个月前 IP:四川
824425

这种问题,经常出现在以下条件下:

  1. 程序是GUI程序
  2. 程序用VC++2002以上版本的MFC
  3. 程序使用了CString的字符串转换功能,比如用CStringA::LoadStringA从资源段字符串表加载字符串

最终的原因:

  1. 程序是GUI程序
  2. 程序使用CP_THREAD_ACP虚拟代码页转换字符串,而不是使用正确的CP_ACP
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,6年5个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的