Что может привести к тому, что файл XML будет заполнен нулевыми символами?
Это сложный вопрос. Я подозреваю, что для ответа потребуются определенные знания файловых систем.
У меня есть приложение WPF "App1", предназначенное для.NET Framework 4.0. Оно имеет Settings.settings
файл, который генерирует стандарт App1.exe.config
файл, в котором хранятся настройки по умолчанию. Когда пользователь изменяет настройки, изменения идут в AppData\Roaming\MyCompany\App1\X.X.0.0\user.config
, Это все стандартное поведение.NET. Тем не менее, иногда мы обнаружили, что user.config
файл на компьютере клиента не тот, каким он должен быть, что приводит к сбою приложения.
Проблема выглядит так: user.config
это размер, который должен быть, если бы он был заполнен XML, но вместо XML это просто набор символов NUL. Это символ 0 повторяется снова и снова. Мы не располагаем информацией о том, что произошло до этой модификации файла.
Мы можем исправить эту проблему на устройстве клиента, если мы просто удалим user.config
потому что Common Language Runtime просто сгенерирует новый. Они потеряют изменения, внесенные в настройки, но изменения могут быть внесены снова.
Однако я столкнулся с этой проблемой в другом приложении WPF, "App2", с другим файлом XML, info.xml
, На этот раз все по-другому, потому что файл генерируется моим собственным кодом, а не CLR. Общими темами являются то, что оба являются приложениями C# WPF, оба являются файлами XML, и в обоих случаях мы совершенно не можем воспроизвести проблему в нашем тестировании. Может ли это быть как-то связано с тем, как приложения C# взаимодействуют с XML-файлами или файлами в целом?
Мы не только не можем воспроизвести проблему в наших текущих приложениях, но я даже не могу воспроизвести проблему, написав собственный код, который специально генерирует ошибки. Я не могу найти ни одной ошибки сериализации XML или ошибки доступа к файлу, которая приводит к тому, что файл заполнен нулями. Так что же может происходить?
Доступ к App1 user.config
позвонив Upgrade()
а также Save()
и получая и устанавливая свойства. Например:
if (Settings.Default.UpgradeRequired)
{
Settings.Default.Upgrade();
Settings.Default.UpgradeRequired = false;
Settings.Default.Save();
}
Доступ к App2 info.xml
путем сериализации и десериализации XML:
public Info Deserialize(string xmlFile)
{
if (File.Exists(xmlFile) == false)
{
return null;
}
XmlSerializer xmlReadSerializer = new XmlSerializer(typeof(Info));
Info overview = null;
using (StreamReader file = new StreamReader(xmlFile))
{
overview = (Info)xmlReadSerializer.Deserialize(file);
file.Close();
}
return overview;
}
public void Serialize(Info infoObject, string fileName)
{
XmlSerializer writer = new XmlSerializer(typeof(Info));
using (StreamWriter fileWrite = new StreamWriter(fileName))
{
writer.Serialize(fileWrite, infoObject);
fileWrite.Close();
}
}
Мы столкнулись с проблемой как в Windows 7, так и в Windows 10. При исследовании этой проблемы я наткнулся на этот пост, где та же проблема XML встречалась в Windows 8.1: сохраненные файлы иногда содержат только NUL-символы
Могу ли я что-то изменить в своем коде, чтобы предотвратить это, или проблема слишком глубоко связана с поведением.NET?
Мне кажется, что есть три возможности:
- CLR записывает нулевые символы в файлы XML.
- Указатель адреса памяти файла переключается в другое место без перемещения содержимого файла.
- Файловая система пытается переместить файл на другой адрес памяти, и содержимое файла перемещается, но указатель не обновляется.
Я чувствую, что 2 и 3 более вероятны, чем 1. Вот почему я сказал, что это может потребовать дополнительных знаний о файловых системах.
Я был бы очень признателен за любую информацию, которая может помочь мне воспроизвести, исправить или обойти проблему. Спасибо!
5 ответов
Нет задокументированной причины такого поведения, так как это происходит с пользователями, но никто не может сказать происхождение этих странных условий.
Это может быть проблемой CLR, хотя это очень маловероятно, CLR не просто записывает нулевые символы, а документ XML не может содержать нулевые символы, если для узлов не определен xsi: nil.
В любом случае, единственный документированный способ исправить это - удалить поврежденный файл, используя следующую строку кода:
try
{
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
}
catch (ConfigurationErrorsException ex)
{
string filename = ex.Filename;
_logger.Error(ex, "Cannot open config file");
if (File.Exists(filename) == true)
{
_logger.Error("Config file {0} content:\n{1}", filename, File.ReadAllText(filename));
File.Delete(filename);
_logger.Error("Config file deleted");
Properties.Settings.Default.Upgrade();
// Properties.Settings.Default.Reload();
// you could optionally restart the app instead
}
else
{
_logger.Error("Config file {0} does not exist", filename);
}
}
Это восстановит user.config, используя Properties.Settings.Default.Upgrade();
снова без нулевых значений.
Общеизвестно, что это может произойти при потере мощности. Это происходит после кэшированной записи, которая расширяет файл (это может быть новый или существующий файл), и вскоре после этого происходит сбой питания. В этом сценарии файл имеет 3 ожидаемых возможных состояния, когда машина возвращается в исходное состояние:
1) Файл вообще не существует или имеет исходную длину, как будто запись никогда не происходила.
2) Файл имеет ожидаемую длину, как будто запись произошла, но данные равны нулю.
3) Файл имеет ожидаемую длину и правильные данные, которые были записаны.
Состояние 2 - это то, что вы описываете. Это происходит потому, что когда вы выполняете кэшированную запись, NTFS изначально просто соответственно увеличивает размер файла, но оставляет VDL (допустимая длина данных) без изменений. Данные за пределами VDL всегда читаются как нули. Данные, которые вы намеревались записать, хранятся в памяти в файловом кеше. В конечном итоге он будет записан на диск, обычно в течение нескольких секунд, и после этого VDL перейдет на диск, чтобы отразить записанные данные. Если потеря питания происходит до записи данных или до увеличения VDL, вы окажетесь в состоянии 2.
Это довольно легко воспроизвести, например, скопировав файл (механизм копирования использует кэшированные записи), а затем сразу же отключив питание от компьютера.
У меня была похожая проблема, и я смог отследить свою проблему на поврежденном жестком диске.
Описание моей проблемы (вся связанная с этим информация):
Диск подключен к материнской плате (SATA):
SSD (система),
3 * HDD.
У одного из жестких дисков были плохие блоки, и были даже проблемы с чтением структуры диска (каталоги и список файлов).
Операционная система: Windows 7 x64
файловая система (на всех дисках): NTFS
Когда система пыталась выполнить чтение или запись на поврежденный диск (запрос пользователя или автоматическое сканирование или по любой другой причине) и попытка не удалась, все операции записи (на другой диск) были некорректными. Файлы, созданные на системном диске (в основном файлы конфигурации другими приложениями), были записаны и были действительны (вероятно, потому что файлы были обналичены в ОЗУ) при прямой проверке содержимого файла.
К сожалению, после перезапуска все файлы (записанные после неудачного доступа на запись / чтение на поврежденном диске) имели правильный размер, но содержимое файлов было "нулевым байтом" (точно так же, как в вашем случае).
Попробуйте исключить проблемы с оборудованием. Вы можете попробовать "скопировать" файл (после изменения) на другой компьютер (загрузить на web / ftp). Или попробуйте сохранить определенный контент в фиксированный файл. Когда файл проверки на другом будет правильным, или когда файл с фиксированным содержимым будет "пустым", причина, вероятно, на локальной машине. Попробуйте заменить компоненты HW или переустановите систему.
Я столкнулся с подобной проблемой, но это было на сервере. Сервер перезагружался во время записи программы в файл, в результате чего файл содержал все нулевые символы и становился непригодным для записи / чтения программой из него.
Логи показали, что сервер перезапустился:
Поврежденный файл показал, что он был последний раз обновлен во время перезапуска:
У меня та же проблема, в конце сериализованного XML-файла есть дополнительный символ "NUL":
Я использую XMLWriter, как это:
using (var stringWriter = new Utf8StringWriter())
{
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, IndentChars = "\t", NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace }))
{
xmlSerializer.Serialize(xmlWriter, data, nameSpaces);
xml = stringWriter.ToString();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
if (removeEmptyNodes)
{
RemoveEmptyNodes(xmlDocument);
}
xml = xmlDocument.InnerXml;
}
}