Подтвердить что ты не робот

С# не будет компилировать длинную строку const с \0 в начале

Я столкнулся с особым случаем, когда я получаю следующую ошибку при создании определенных типов строк:

Непредвиденная ошибка отладки информации об ошибке - "Ошибка HRESULT E_FAIL была возвращена из вызова COM-компонента".

Эта ошибка не является новой для (см. этот вопрос и этот вопрос), но представленные проблемы не имеют ничего общего с этим.

Для меня это происходит, когда я создаю строку const определенной длины, которая содержит нуль-завершающий символ (\0) где-то рядом с началом.

Для воспроизведения сначала создайте строку соответствующей длины, например. с помощью:

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

Возьмите результирующую строку во время выполнения (например, "Немедленное окно" или путем зависания переменной и копирования ее значения). Затем сделайте из него const:

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

Наконец, поместите a \0 где-нибудь:

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

Некоторые вещи, которые я заметил:

  • если вы положите \0 ближе к концу, ошибка не произойдет.
  • Воспроизводится с использованием .NET Framework 4.6.1 и 4.5
  • Не получается, если строка короткая.
  • Изменить: еще более ценная информация доступна в комментариях ниже.

Любая идея, почему это происходит? Это какая-то ошибка?

Изменить: Ошибка, включая информацию из комментариев. Спасибо всем.

4b9b3361

Ответ 1

Я немного поработаю над этой проблемой. Эта проблема возникает как в VS2015, так и в более ранних версиях. Таким образом, ничего не происходит непосредственно с самим компилятором С#, это происходит неправильно в методе ISymUnmanagedWriter2:: DefineConstant2(). ISymUnmanagedWriter2 - это COM-интерфейс, который является частью инфраструктуры .NET, которую используют все компиляторы. И используется как Roslyn, так и унаследованным компилятором С#.

Комментарии в исходном коде Roslyn (фактически восходят к проекту CCI), который использует этот метод, достаточно освещаются, что проблемы с этим методом были обнаружены раньше:

// 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. Но, надеюсь, вы видите письменность на стене, вероятность того, что она будет исправлена, довольно мала. Это код, который больше никто не поддерживает, теперь он имеет статус "недокументированный", и, судя по другим комментариям, это происходит задолго до .NET. Не Эд Маурер тоже:)

Обходной путь должен быть достаточно простым, приклеить эту строку вместе во время выполнения.

Ответ 2

Я смог воспроизвести проблему как закодированную. Затем я изменил объявление на:

const string history = @ "aaa\0aaa... много и много aaa... aaa";

Пробовал снова, и он просто компилируется.