为什么当我将输出编码设置为UTF8时,csc.exe会崩溃?
为什么当我将输出编码设置为UTF8时,csc.exe会崩溃?
我遇到了一个非常奇怪的问题。
我想知道其他人是否也遇到了这个问题,以及为什么会发生。
我运行了一行代码的程序,代码如下:System.Console.WriteLine(System.Console.OutputEncoding.EncodingName);
,我发现编码是西欧(DOS)
。
好的
这是一些代码页的列表
1200 Unicode
和 65001 utf-8
和 Windows-1252 Western European (Windows)
和 850 Western European DOS
,链接在https://msdn.microsoft.com/en-us/library/system.text.encoding(v=vs.110).aspx
假设我编写一个C#程序来将编码更改为UTF-8
class sdf { static void Main(string[] args) { System.Console.WriteLine(System.Console.OutputEncoding.EncodingName); System.Console.OutputEncoding=System.Text.Encoding.GetEncoding(65001); System.Console.WriteLine(System.Console.OutputEncoding.EncodingName); } }
它可以正常工作,输出为
西欧(DOS) Unicode (UTF-8)
现在,当我再次运行csc时,csc崩溃了。
我用memtest检查了我的RAM 14个小时,8次通过,硬盘上的chkdsk也运行正常。这肯定不是这些问题,而是编码问题。
我知道这是编码问题,因为如果我打开一个新的cmd提示符,然后运行csc,它就不会崩溃。
因此,运行这个C#程序会改变shell,以至于下一次只运行csc就会导致csc本身崩溃,以这种大的方式。
如果我编译下面的代码,然后运行它,然后运行csc,然后再次运行csc或csc whatever.cs,我会让csc崩溃。
关闭cmd提示符,打开一个新的。
这次,试着注释和取消注释程序的第二行。
我发现如果第二行(将代码页更改为850(DOS西欧)的行)存在,那么下一次运行csc时它就不会崩溃。
而如果我注释掉那个第二行,这样程序在运行时将代码页/编码更改为UTF-8,然后下一次运行csc时csc就会崩溃。
// 取消注释最后一行,然后
// 这次运行正常,但会导致下一次csc崩溃。
class asdf { static void Main() { System.Console.OutputEncoding = System.Text.Encoding.UTF8; //output and to utf8 System.Console.OutputEncoding=System.Text.Encoding.GetEncoding(850); } }
我不是唯一一个遇到这种问题的人,尽管在那里没有找到解释 https://social.msdn.microsoft.com/Forums/vstudio/en-US/0e5f477e-0c32-4e88-acf7-d53d43d5b566/c-command-line-compiler-cscexe-immediately-crashes-when-run-in-code-page-65001-utf8?forum=csharpgeneral
我可以通过确保最后一行将代码页设置为850来处理它。尽管如我所述,这是一个不完善的解决方案。
另外,我想知道其他人是否也遇到了这个CSC的问题,或者是否有其他解决方案。
添加
uuu1.cs
// uuu1.cs class asdf { static void Main() { System.Console.InputEncoding = System.Text.Encoding.UTF8; System.Console.OutputEncoding = System.Text.Encoding.UTF8; // 不是unicode。UTF8表示重定向将正常工作 System.Console.WriteLine("ჵ"); // 尝试重定向.. // 并尝试检查是否会导致csc崩溃 //System.Console.OutputEncoding=System.Text.Encoding.GetEncoding(850); //System.Console.InputEncoding =System.Text.Encoding.GetEncoding(850); //问题是当它被注释掉时,它会破坏重定向 } }
取消注释最后一行,即添加
System.Console.OutputEncoding=System.Text.Encoding.GetEncoding(850);
将会阻止崩溃,但这是一个不完善的解决方案,因为例如...如果我想将程序的输出重定向到一个文件,那么我需要从开始到结束都使用UTF8,否则它不起作用。
这个解决方案在将代码页设置为850时可以工作
c:\blah>uuu1>r.rc:\blah>type r.r c:\blah>ჵ
如果我取消注释最后一行,即将代码页更改为850,则csc不会在下一次运行时崩溃,但重定向不起作用,r.r中不包含该字符。
添加2
Han的答案让我注意到另一种触发此错误的方式
C:\Users\harvey\somecs3>cscMicrosoft (R) Visual C# Compiler version 4.0.30319.18408 for Microsoft (R) .NET Framework 4.5 Copyright (C) Microsoft Corporation. All rights reserved. warning CS2008: No source files specified error CS1562: Outputs without source must have the /out option specified C:\Users\harvey\somecs3>chcp 65001 Active code page: 65001 C:\Users\harvey\somecs3>csc <-- CRASH C:\Users\harvey\somecs3>
csc.exe在将输出编码设置为UTF8时为什么会崩溃?出现这个问题的原因是C#编译器在将文本输出到控制台时处理方式存在错误。它具有自诊断功能,以确保从UTF-16编码的字符串转换为控制台输出代码页时工作正常,当出现问题时会触发错误处理程序。具体的崩溃信息如下:
csc.exe!OnCriticalInternalError() + 0x4 bytes
csc.exe!ConsoleOutput::WideToConsole() + 0xdc51 bytes
csc.exe!ConsoleOutput::print_internal() + 0x2c bytes
csc.exe!ConsoleOutput::print() + 0x80 bytes
csc.exe!ConsoleOutput::PrintString() + 0xb5 bytes
csc.exe!ConsoleOutput::PrintBanner() + 0x50 bytes
csc.exe!_main() + 0x2d0eb bytes
下面是WideToConsole()方法的代码:
/*
* Like WideCharToMultiByte, but translates to the console code page. Returns length,
* INCLUDING null terminator.
*/
int ConsoleOutput::WideCharToConsole(LPCWSTR wideStr, LPSTR lpBuffer, int nBufferMax)
{
if (m_fUTF8Output) {
if (nBufferMax == 0) {
return UTF8LengthOfUnicode(wideStr, (int)wcslen(wideStr)) + 1; // +1 for nul terminator
}
else {
int cchConverted = NULL_TERMINATED_MODE;
return UnicodeToUTF8 (wideStr, &cchConverted, lpBuffer, nBufferMax);
}
}
else {
return WideCharToMultiByte(GetConsoleOutputCP(), 0, wideStr, -1, lpBuffer, nBufferMax, 0, 0);
}
}
/*
* Convert Unicode string to Console ANSI string allocated with VSAlloc
*/
HRESULT ConsoleOutput::WideToConsole(LPCWSTR wideStr, CAllocBuffer &buffer)
{
int cch = WideCharToConsole(wideStr, NULL, 0);
buffer.AllocCount(cch);
if (0 == WideCharToConsole(wideStr, buffer.GetData(), cch)) {
VSFAIL("How'd the string size change?");
// We have to NULL terminate the output because WideCharToMultiByte didn't
buffer.SetAt(0, '\0');
return E_FAIL;
}
return S_OK;
}
崩溃发生在VSFAIL()断言附近,从机器代码可以判断。可以看到返回E_FAIL的语句。然而,与我发布的版本不同,if()语句被修改了,而且看起来VSFAIL()被RETAILVERIFY()替换了。在进行这些更改时出现了问题,可能是在现在被命名为UTF16ToUTF8()的UnicodeToUTF8()方法中出了问题。需要强调的是,我发布的版本实际上没有崩溃,你可以通过运行C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe来亲自验证。只有v4版本的csc.exe存在这个bug。
关于实际的bug,很难从机器代码中找出来,最好让微软来解决这个问题。你可以在connect.microsoft.com上报告这个bug,但我没有找到类似的报告。这个bug的解决方法是使用CHCP命令将代码页改回来。
我的版本是Microsoft (R) Visual C# Compiler version 4.0.30319.18408。
我看到的唯一的“提交bug”按钮在microsoft.net native页面上,connect.microsoft.com/VisualStudio/MSNetNative,但那只会跳转到一个错误页面,试图提交bug的时候出错了!i.imgur.com/Rxy8bW9.png,也许还有其他地方可以提交bug,如果你看到了提交bug的选项,请告诉我。
当我尝试时,看起来没问题。你必须登录才能进行提交。
与之相对比,如果我在目录中点击powershell,就会有一个提交bug的按钮。如果我点击visual studio,就会收到一条消息,上面写着“您已被邀请加入Visual Studio的私人NDA计划,您可以通过在页面顶部的下拉菜单“程序”中进行选择来查看特定内容。”在那个时候,我不知道下一步该点击什么,但点击“程序”似乎并没有帮助太多。
你可以提交这个bug报告,如果你看到了提交bug的选项,请告诉我。顺便说一句,如果已经运行了chcp 850,那么chcp命令就会显示代码页为850。所以为什么chcp 850会有所区别,我不知道,如果chcp命令已经显示为850,为什么还需要chcp 850。我现在已经提交了一个bug报告。但实际上,你必须先登出,然后转到connect.microsoft.com/VisualStudio,然后点击“提交bug”,然后提示登录,然后填写反馈表格。但在登录的情况下访问该URL会出现错误。webapps.stackexchange.com/questions/79495/…
你的代码只改变了输出代码页,即SetConsoleOutputCP,但是运行chcp.com只检查输入代码页,即GetConsoleCP。运行chcp.com 850会同时修改输入和输出代码页,即调用SetConsoleCP和SetConsoleOutputCP。另外,使用控制台中的代码页65001在不同版本的Windows中存在许多错误,从XP到Windows 10都有一些错误,其中一些错误是在conhost.exe(在Windows 8+中可能是condrv.sys)中,还有一些错误是在C运行时或其他库中。如果你需要在控制台中使用Unicode,应该使用[W]ide API。
非常有趣的观点,关于chcp只检查输入的观点。我在[W]ide API上找不到太多的信息。我如何在C#中使用"[W]ide API"来设置代码页为UTF8?
将输入和输出编码设置为System.Text.Encoding.Unicode。使用这个设置,Console.ReadLine调用Win32的ReadConsoleW,Console.WriteLine调用Win32的WriteConsoleW。注意,非Unicode编码调用ReadFile,它对于一行开头的Ctrl+Z有特殊处理,但ReadConsole没有。
谢谢。关于System.Text.Encoding.Unicode导致Console.ReadLine调用Win32 ReadConsoleW的信息在哪里可以找到?System.Console.InputEncoding=System.Text.Encoding.UTF8,我注意到chcp仍然显示为65001。并且仍然存在崩溃的csc bug。使用.Unicode时,chcp不会改变,csc不会崩溃。但是,如果我执行myprog>a.a,如果不使用UTF8,它不会重定向特殊字符。
当你重定向到管道或文件时,它会写入没有BOM(即"\uFEFF")的UTF-16,所以有些程序可能无法检测到文本的编码。如果System.Console.IsOutputRedirected,你可以在这种情况下写入BOM,或者切换到UTF-8,如果你更喜欢。
csc.exe在输出编码设置为UTF8时为什么会崩溃?出现这个问题的原因是Windows控制台存在许多与Unicode相关的错误。解决方法是使用utf8output参数并将输出重定向到文件。具体操作为,在运行csc命令时加上/utf8output参数并将输出重定向到文件。需要注意的是,只有同时使用了这两个操作,才能使CSC在UTF-8控制台下正常工作。这个问题的根本原因是输出编码为UTF-8时,再次运行csc命令会导致崩溃。因此,使用utf8output参数和输出重定向可以解决这个问题。具体操作的示例代码如下:
csc /utf8output aaa1.cs > aaa1-compilation.log
这样就可以将CSC的输出重定向到名为aaa1-compilation.log的文件中。通过这种方式可以避免CSC在UTF-8控制台下的崩溃问题,确保程序能够正常运行。