My application opens an XML file in a folder to deserialize its contents. I release everything, but devenv still keeps a handle on the containing folder. Problem is: I open 3rd-party programs as child processes and their execution will fail, since they moan about not being able to delete the folder.
This is the code I use in my application to open the file and deserialize the XML contents:
XmlConfig result = null;
XmlSerializer serializer = new XmlSerializer(typeof(XmlConfig));
using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
result = (XmlConfig)serializer.Deserialize(sr);
}
}
return result;
Before, I didn't use the FileStream but only the StreamReader, but the result was the same.
The error occurs, if:
I start the debug session from within Visual Studio
I run the application from the output folder and attach the debugger before running the code above
I run the application from the output folder and do not attach the debugger, but the error occurred before
The error does not occur, if:
I run the application from the output folder without attaching any debugger
I run the application from the output folder and attach the debugger after running the code above
I'm running VS Professional 2017 15.8.4. as administrator (since the application has to run with admin privileges) It's a WPF project, .Net version 4.6.
All I could find on this topic is about not being able to build because of Visual Studio locking some dll file in the bin or obj folder. That doesn't match my problem.
What happens here and more important: how can I solve that problem?
Edit:
Here's the error message I get from my child process:
devenx.exe pid: 8612 type: file 1388: C:\lockedfolder
I also checked that with Unlocker:
So as I was asked to I'm gonna formulate my comment as an answer.
Big disclaimer: this problem in itself still remains a mistery to me as I couldn't to my best efforts reproduce it in any form.
I threw together a little test program:
Example
class Program
{
public static string filepath = "test.xml";
static void Main(string[] args)
{
Serialize();
Console.WriteLine(Deserialize().Test);
Console.ReadKey(true);
}
private static XmlConfig Deserialize()
{
XmlConfig result = null;
XmlSerializer serializer = new XmlSerializer(typeof(XmlConfig));
using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
result = (XmlConfig)serializer.Deserialize(sr);
}
}
return result;
}
private static void Serialize()
{
using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate))
{
XmlSerializer serializer = new XmlSerializer(typeof(XmlConfig));
serializer.Serialize(fs, new XmlConfig());
}
}
}
public class XmlConfig
{
public string Test { get; set; } = "Teststring";
}
This should not have worked if the problem was in the code. But it did execute fine without any problems. Debug/Release, with/without attached debugger, VS as Admin/Non-Admin.
Advices
So here are my advices which seemed to help solve the question which I always recommend trying when working with streams/serialization:
Even though Streams like FileStream implement IDisposable and should flush+close the stream when used in a using-block try to manually flush and or close them. This can sometimes do the trick (especially when working with COM-Ports)
Always try to restrict local variables to the smallest scope possible (i.e. move Serializer initialization inside the using it is needed.
Less configuration > more configuration that goes along with reducing complexity as much as possible (For this compare Serialize() and Deserialize() in my example)
Related
I'm trying to build a small program to monitor my pfirewall.log, but I can't seem to open it.
I found quite many (simple) answers, that all kinda say
// use FilesystemWatcher
// open FileStream
// read from last position to end
// output new lines
The problem here is: The file seems to always be opened by another process already. I guess that's the windows process writing to the file, since it's getting written to all the time, as Notepad++ shows me.
Which means, Notepad++ can for some reason do what I can not: Read the file despite it being opened already.
I initialize my monitor in the constructor:
public FirewallLogMonitor(string path)
{
if (!File.Exists(path))
throw new FileNotFoundException("Logfile not found");
this.file = path;
this.lastPosition = 0;
this.monitor = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path));
this.monitor.NotifyFilter = NotifyFilters.Size;
}
And try to read the file on monitor.Changed event:
private void LogFileChanged(object sender, FileSystemEventArgs e)
{
using (FileStream stream = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader reader = new StreamReader(stream))
{
stream.Seek(this.lastPosition, SeekOrigin.Begin);
var newLines = reader.ReadToEnd();
this.lastPosition = stream.Length;
var filteredLines = filterLines(newLines);
if (filteredLines.Count > 0)
NewLinesAvailable(this, filteredLines);
}
}
It always throws the IOException on new FileStream(...) to tell me the file is already in use.
Since Notepad++ does it, there has to be a way I can do it too, right?
**Edit: ** A button does this:
public void StartLogging()
{
this.IsRunning = true;
this.monitor.Changed += LogFileChanged;
this.monitor.EnableRaisingEvents = true;
}
**Edit2: ** This is not a duplicate of FileMode and FileAccess and IOException: The process cannot access the file 'filename' because it is being used by another process, since that one assumes I have control over the writing process. Will try the other suggestions, and report back with results.
If i understand your question you can use the notepad++ itself with a plugin to monitor you need to go to:
plugins -> Document Moniter -> Start to monitor
if you dont have this plugin you can download it here:
http://sourceforge.net/projects/npp-plugins/files/DocMonitor/
I'm attempting to use StreamReader and StreamWriter to grab a temporary output log (.txt format) from another application.
The output log is always open and constantly written to.
Unhelpfully if the application closes or crashes, the log file ends up deleted - hence the need for a tool that can grab the information from this log and save it.
What my program currently does is:
Create a new .txt file, and stores the path of that file as the
string "destinationFile".
Finds the .txt log file to read, and stores the path of that file as
the string "sourceFile"
It then passes those two strings to the method below.
Essentially I'm trying to read the sourceFile one line at a time.
Each time one line is read, it is appended to destinationFile.
This keeps looping until the sourceFile no longer exists (i.e. the application has closed or crashed and deleted its log).
In addition, the sourceFile can get quite big (sometimes 100Mb+), and this program may be handling more than one log at a time.
Reading the whole log rather than line by line will most likely start consuming a fair bit of memory.
private void logCopier(string sourceFile, string destinationFile)
{
while (File.Exists(sourceFile))
{
string textLine;
using (var readerStream = File.Open(sourceFile,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
using (var reader = new StreamReader(readerStream))
{
while ((textLine = reader.ReadLine()) != null)
{
using (FileStream writerStream = new FileStream(destinationFile,
FileMode.Append,
FileAccess.Write))
using (StreamWriter writer = new StreamWriter(writerStream))
{
writer.WriteLine(textLine);
}
}
}
}
}
The problem is that my WPF application locks up and ceases to respond when it reaches this code.
To track down where, I put a MessageBox just before the writerStream line of the code to output what the reader was picking up.
It was certainly reading the log file just fine, but there appears to be a problem with writing it to the file.
As soon as it reaches the using (FileStream writerStream = new FileStream part of the code, it stops responding.
Is using the StreamWriter in this manner not valid, or have I just gone and dome something silly in the code?
Am also open to a better solution than what I'm trying to do here.
Simply what I understand is you need to copy a file from source to destination which may be deleted at any time.
I'll suggest you to use FileSystemWatcher to watch for source file changed event, then just simply copy the whole file from source to destination using File.Copy.
I've just solved the problem, and the issue was indeed something silly!
When creating the text file for the StreamWriter, I had forgotten to use .Dispose();. I had File.Create(filename); instead of File.Create(filename).Dispose(); This meant the text file was already open, and the StreamWriter was attempting to write to a file that was locked / in use.
The UI still locks up (as expected), as I've yet to implement this on a new thread as SteenT mentioned. However the program no longer crashes and the code correctly reads the log and outputs to a text file.
Also after a bit of refinement, my log reader/writer code now looks like this:
private void logCopier(string sourceFile, string destinationFile)
{
int num = 1;
string textLine = String.Empty;
long offset = 0L;
while (num == 1)
{
if (File.Exists(sourceFile))
{
FileStream stream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (new StreamReader(stream))
{
stream.Seek(offset, SeekOrigin.Begin);
TextReader reader2 = new StreamReader(stream);
while ((textLine = reader2.ReadLine()) != null)
{
Thread.Sleep(1);
StreamWriter writer = new StreamWriter(destinationFile, true);
writer.WriteLine(textLine);
writer.Flush();
writer.Close();
offset = stream.Position;
}
continue;
}
}
else
{
num = 0;
}
}
}
Just putting this code up here in case anyone else is looking for something like this. :)
While troubleshooting a performance problem, I came across an issue in Windows 8 which relates to file names containing .dat (e.g. file.dat, file.data.txt).
I found that it takes over 6x as long to create them as any file with any other extension.
The same issue occurs in windows explorer where it takes significantly longer when copying folders containing .dat* files.
I have created some sample code to illustrate the issue.
internal class DatExtnIssue
{
internal static void Run()
{
CreateFiles("txt");
CreateFiles("dat");
CreateFiles("dat2");
CreateFiles("doc");
}
internal static void CreateFiles(string extension)
{
var folder = Path.Combine(#"c:\temp\FileTests", extension);
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
var sw = new Stopwatch();
sw.Start();
for (var n = 0; n < 500; n++)
{
var fileName = Path.Combine(folder, string.Format("File-{0:0000}.{1}", n, extension));
using (var fileStream = File.Create(fileName))
{
// Left empty to show the problem is due to creation alone
// Same issue occurs regardless of writing, closing or flushing
}
}
sw.Stop();
Console.WriteLine(".{0} = {1,6:0.000}secs", extension, sw.ElapsedMilliseconds/1000.0);
}
}
Results from creating 500 files with the following extensions
.txt = 0.847secs
.dat = 5.200secs
.dat2 = 5.493secs
.doc = 0.806secs
I got similar results using:
using (var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{ }
and:
File.WriteAllText(fileName, "a");
This caused a problem as I had a batch application which was taking far too long to run. I finally tracked it down to this.
Does anyone have any idea why this would be happening? Is this by design? I hope not, as it could cause problems for high-volume application creating .dat files.
It could be something on my PC but I have checked the windows registry and found no unusual extension settings.
If all else fails, try a kludge:
Write all files out as .txt and then rename *.txt to .dat. Maybe it will be faster :)
I have simple page, that loads XML from filesystem, fills textboxes, these can be updated and saved. For serializing and deserializing I am using these methods:
private static readonly object FormDataLock = new object();
public static FormData getFormData(string filename)
{
FormData fd;
lock (FormDataLock)
{
XmlSerializer x = new XmlSerializer(typeof(FormData));
using (Stream s = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
return (FormData)x.Deserialize(s);
}
}
}
public void saveFormData(string filename)
{
lock (FormDataLock)
{
XmlSerializer x = new XmlSerializer(typeof(FormData));
using (Stream s = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
x.Serialize(s, this);
}
}
}
But the problem is, that I am gettig sometimes (as I have notticed when I click the "save" button too fast after PageLoad) the IOException:
IOException: The process cannot access the file ".." because it is being used by another process.
I was trying to lock the block with mutex, but it is still not working properly. The page form is quite simple, but I am using UpdatePanel on it (is it important?).
When the page is loaded and first save request was done OK, I can click the button as fast as I can and everything is OK (no exception).
XmlSerialization creates new dll's on the fly which are specific to the class you're trying to serialise in the temp directory. These are created to increase performance.
See http://msdn.microsoft.com/en-us/library/swxzdhc0.aspx
Instead of calling the GC.Collect etc... try creating the serializer as a static field on your class. This should improve performance and might solve your problem as it's only ever going to be created once.
This code will create a single xmlserializer in a thread safe way. Do NOT add a [ThreadStatic] attribute to this as this will ensure the code gets executed once per thread and make it thread unsafe again!
private static readonly XmlSerializer xmlSerializer =
new XmlSerializer(typeof(FormData));
I had similar problem and I hope this will help you too.
The problem was that garbage collector didn't clean up before your second click, so you should try to call it manually. Try to call GC before living using
GC.Collect();
GC.WaitForPendingFinalizers();
The StreamReader locks a text file whilst it is reading it.
Can I force the StreamReader to work in a "read-only" or "non locking" mode?
My workaround would be to copy the file to a temp location and read it from there but I would prefer to use the StreamReader directly if possible.
Any alternative suggetions?
Background:
I've written a small app to get some stats out of a log file. This file is constantly being updating (several times a second) by an outside program lets call AAXXYY.
Reviewing the output suggests that my app may be locking the file and preventing AAXXYY from writing.
This is what I'm doing
private void btnGetStats_Click(object sender, EventArgs e)
{
int countStarts = 0;
int countEnds = 0;
IList<string> sessions = new List<string>();
using(StreamReader stRead = new StreamReader(openFileDialog1.FileName,Encoding.Unicode))
{
while(!stRead.EndOfStream)
{
string line = stRead.ReadLine();
if(line.Contains("Session start"))
{
countStarts++;
sessions.Add(line.Substring(line.IndexOf("["), line.LastIndexOf("]") - line.IndexOf("[")));
}
if (line.Contains("Session end"))
{
countEnds++;
sessions.Remove(line.Substring(line.IndexOf("["), line.LastIndexOf("]") - line.IndexOf("[")));
}
}
}
txtStarts.Text = countStarts.ToString();
txtEnds.Text = countEnds.ToString();
txtDifference.Text = (countStarts - countEnds).ToString();
listBox1.DataSource = sessions;
}
You can pass a FileStream to the StreamReader, and create the FileStream with the proper FileShare value. For instance:
using (var file = new FileStream (openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var reader = new StreamReader (file, Encoding.Unicode)) {
}
Thought I'd add some context, StreamReader does not lock a file for reading only for writing whist it is being read. Take a look at the code below from the StreamReader class.
new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.SequentialScan);
Notice the default FileAccess.Read parameter taken for MSDN http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx
Allows subsequent opening of the file for reading. If this flag is not specified, any request to open the file for reading (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.
Again taken from MSDN to allow reading and writing use FileAccess.ReadWrite instead (as suggested by Jb Evain).
Allows subsequent opening of the file for reading or writing. If this
flag is not specified, any request to open the file for reading or
writing (by this process or another process) will fail until the file
is closed. However, even if this flag is specified, additional
permissions might still be needed to access the file.