【问题标题】:C# won't compile a long const string with a \0 near the beginningC# 不会编译开头附近有 \0 的长 const 字符串
【发布时间】:2016-01-10 14:15:25
【问题描述】:

我遇到了一个特殊情况,在创建某些类型的字符串时出现以下错误:

写入调试信息时出现意外错误——“错误 HRESULT E_FAIL 已从对 COM 组件的调用中返回。”

这个错误对于 Stack Overflow 来说并不新鲜(见this questionthis question),但出现的问题与这个无关。

对我来说,当我创建一个特定长度的 const 字符串时会发生这种情况,该字符串在开头附近的某处包含一个空终止字符 (\0)。

要重现,首先生成一个适当长度的字符串,例如使用:

var s = new string('a', 3000);

在运行时获取结果字符串(例如,立即窗口或将鼠标悬停在变量上并复制其值)。然后,用它制作一个const

const string history = "aaaaaa...aaaaa";

最后,在某处放一个\0

const string history = "aaaaaaaaaaaa\0aa...aaaaa";

我注意到的一些事情:

  • 如果您将\0 放在末尾附近,则不会发生错误。
  • 使用 .NET Framework 4.6.1 和 4.5 复制
  • 如果字符串很短,则不会发生。
  • 编辑:下面的 cmets 中提供了更多宝贵信息。

知道为什么会这样吗?这是某种错误吗?

编辑Bug 归档,包括来自 cmets 的信息。谢谢大家。

【问题讨论】:

  • 使用 Notepad++ 和一些试验和错误,似乎字符串的第 2033 个索引是这个问题的神奇编译点而不是编译点。这是使用 VS 2015 社区、win10 build 11092、.net 4.5.2 或 .net 4.6.1
  • 哦,它肯定是某种错误!错误消息中的其他提示表明它在哪里——在 PDB 编写器的非托管代码中。有人可能将一个以空字符结尾的字符串与一个以长度为前缀的字符串混为一谈。这是一个容易犯的错误。
  • 我不明白这如何使它偏离主题。这是一个实际遇到的编程问题。它是一个工具的错误这一事实并没有使它偏离主题。其他人仍然可以遇到同样的问题并来这里寻找答案。 Gigi,既然您已经提交了错误报告,请考虑发布对此效果的答案以及指向您的错误报告的链接。如果并且当您收到回复时,您可以编辑您的答案以包含该信息。 (回答你自己的问题完全没问题,在这种情况下很有意义。)
  • \0 表示字符串已终止(从 c 和 c++ 天开始,此字符串称为空终止字符串以标识字符串的结尾)。我认为将 \0 放在字符串的中间会以某种方式混淆调试器并导致错误。但我不明白你为什么要在字符串中间使用 \0 甚至 C#!
  • @Hesham 这是一个罕见的用例,但我正在对二进制字符串做一些特殊的事情,其中​​包括 NUL 字符。

标签: c# string


【解决方案1】:

我会稍微讨论一下这个问题。 VS2015 及更早版本都会出现此问题。所以与C#编译器本身没有直接关系,这在ISymUnmanagedWriter2::DefineConstant2()实现方法中出错了。 ISymUnmanagedWriter2 是一个 COM 接口,是所有编译器都使用的 .NET 基础结构的一部分。并被 Roslyn 和旧版 C# 编译器使用。

使用该方法的Roslyn源代码(其实可以追溯到CCI project)中的cmets足够有启发性,之前发现该方法有问题:

// EDMAURER If defining a string constant and it is too long (length limit is undocumented), this method throws
// an ArgumentException.
// (see EMITTER::EmitDebugLocalConst)

try
{
    this.symWriter.DefineConstant2(name, value, constantSignatureToken);
}
catch (ArgumentException)
{
    // writing the constant value into the PDB failed because the string value was most probably too long.
    // We will report a warning for this issue and continue writing the PDB.
    // The effect on the debug experience is that the symbol for the constant will not be shown in the local
    // window of the debugger. Nor will the user be able to bind to it in expressions in the EE.

    //The triage team has deemed this new warning undesirable. The effects are not significant. The warning
    //is showing up in the DevDiv build more often than expected. We never warned on it before and nobody cared.
    //The proposed warning is not actionable with no source location.
}
catch (Exception ex)
{
    throw new PdbWritingException(ex);
}

吞咽异常,tsk,tsk。在您的案例中,它死于最后一个 catch 子句。他们确实深入挖掘了对字符串长度问题的逆向工程:

internal const int PdbLengthLimit = 2046; // Empirical, based on when ISymUnmanagedWriter2 methods start throwing.

这与 \0 开始抛出的位置相当接近,我得到了 2034。当然,您或这里的其他任何人对此都无能为力。您可以合理地做的就是在 connect.microsoft.com 上报告该错误。但希望你能看到墙上的文字,它得到修复的可能性很小。这是没有人再维护的代码,它现在具有“未记录”状态,并且从其他 cmets 来看,这可以追溯到 .NET 之前很久。也不是 Ed Maurer :)

解决方法应该很简单,在运行时将这个字符串粘合在一起。

【讨论】:

    【解决方案2】:

    我能够按照编码重现问题。然后我将声明更改为:

    const string history = @"aaa\0aaa...很多很多 aaa...aaa";

    再试一次,它编译得很好。

    【讨论】:

    • 当然,当你使用@时,\反斜杠不再特殊了。
    猜你喜欢
    • 1970-01-01
    • 2016-05-15
    • 1970-01-01
    • 2018-10-30
    • 2018-03-14
    • 2020-10-22
    • 2020-03-08
    • 1970-01-01
    • 2020-09-17
    相关资源
    最近更新 更多