I was helping a friend fix a problem that occurred in a VB.Net application. Upon inspection, an XML file was somehow invalid. The last few bytes were wrong.
We found the code that modifies the file. It takes the original file, replaces a string with another string, then writes the file. The code below demonstrates.
Two MessageBoxes are shown, first:
Now is the time for all good people to come to the aid of their country [SUBSTITUTION TEXT]
This is the end!
The second MessageBox:
Now is the time for all good people to come to the aid of their country NewText
This is the end! is the end!
The code replaces “[SUBSTITUTION TEXT]” with “NewText”. Notice that there are extra characters at the end.
Does that give you a clue?
The code opens a stream for both reading and writing. It then reads the entire contents into a string, modifies the string (which makes it shorter) then writes the string to the stream starting at position 0. The length of the original stream has not changed.
This code is a bug that’s difficult to detect: failure detection occurs only under certain conditions. If the replacement text is longer, it works. If it’s shorter and the excess characters are ignored, no problem.
One fix is to add a line that changes the stream length before the writer closes the file:
Another fix is to read from one file and write to another, perhaps renaming after closing.
Yet another fix is to use the XMLDom to parse, modify, write the XML.
This is the sample code with a few variables renamed to protect the innocent<g>.
Dim TempFile As String = IO.Path.GetTempFileName ' or a string like "c:\t.tmp"
Dim cPlaceHolder As String = "[SUBSTITUTION TEXT]"
Dim TempStr As String = "Now is the time for all good people to come to the aid of their country " + _
cPlaceHolder + vbCrLf + "This is the end!"
Dim uni As New System.Text.UnicodeEncoding
Dim UnicodePrefix(1) As Byte ' 2 element array the Unicode prefix: 255, 254
UnicodePrefix(0) = 255
UnicodePrefix(1) = 254
My.Computer.FileSystem.WriteAllBytes(TempFile, UnicodePrefix, False) ' write out prefix
My.Computer.FileSystem.WriteAllBytes(TempFile, uni.GetBytes(TempStr), True)
Dim ascFileContents As String = My.Computer.FileSystem.ReadAllText(TempFile)
System.Diagnostics.Debug.WriteLine("Orig file:" + vbCrLf + ascFileContents.ToString)
Dim MyFileStream As New FileStream(TempFile, FileMode.Open, FileAccess.ReadWrite)
Dim reader As New StreamReader(MyFileStream, System.Text.Encoding.Unicode)
Dim writer As New StreamWriter(MyFileStream, System.Text.Encoding.Unicode)
writer.BaseStream.Position = 0
Dim XMLString As String = reader.ReadToEnd()
XMLString = XMLString.Replace(cPlaceHolder, "NewText")
ascFileContents = My.Computer.FileSystem.ReadAllText(TempFile)
System.Diagnostics.Debug.WriteLine("New file:" + vbCrLf + ascFileContents)