XmlSerializer - File access Exception - c#

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();

Related

Prevent creating compressed file if compression breaks

I am using this code to compress files:
public class GzipCompressor : ICompressor
{
public void Compress(string input, string output)
{
using (FileStream originalFileStream = File.OpenRead(input))
using (FileStream compressedFileStream = File.OpenWrite(output))
using (GZipStream compressor = new GZipStream(compressedFileStream, CompressionMode.Compress))
originalFileStream.CopyTo(compressor);
}
}
The problem is that if someone closes the application while the file is compressing, the file will be saved to the destination path, but it will be invalid (it will not even be able to open it). Is there a way to avoid this? I would prefer such a file not to be created at all in such a situation.
You can try to use Application.ApplicationExit to perform logic when the application is attempting to close down. Keep in mind that there's still a chance something catastrophic is occurring and you wont be able to clean everything up (if the computer is forcibly shut down, there's not much you can do). The documentation also mentions the following got'cha:
Because this is a static event, you must detach any event handlers attached to this event in the ApplicationExit event handler itself. If you do not detach these handlers, they will remain attached to the event and continue to consume memory.
It can be solved by using GetTempFileName. I will provide an example based on your example.
private FileInfo? _tmpFileInfo;
public void Compress(string input, string output)
{
_tmpFileInfo = CreateTempFile();
using (FileStream originalFileStream = File.OpenRead(input))
using (FileStream compressedFileStream = File.OpenWrite(_tmpFileInfo!.FullName))
using (GZipStream compressor = new GZipStream(compressedFileStream, CompressionMode.Compress))
{
originalFileStream.CopyTo(compressor);
}
File.Copy(_tmpFileInfo.FullName, output);
File.Delete(_tmpFileInfo.FullName);
}
So basically what we do, is we create a temporary file and zip it to it, when it is done successfully, we copy it back to the output location. Here is CreateTempFile method:
public FileInfo? CreateTempFile()
{
var fileName = Path.GetTempFileName();
FileInfo? fileInfo = new FileInfo(fileName)
{
Attributes = FileAttributes.Temporary
};
return fileInfo;
}
That is it.
If you need to delete the temp file while the application makes an unhandled exception or process exists or other events, you can create an event like this:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(GzipCompressor.ActionOnExit);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(GzipCompressor.ActionOnExit);
var zip = new GzipCompressor();
zip.Compress(#"C:\temp\in\file1.txt", #"C:\temp\out\file1.txt.gz");
}
Then in your GzipCompressor class make FileInfo static.
private static FileInfo? _tmpFileInfo;
and create a static method to delete the temp file:
public static void ActionOnExit(object? sender, EventArgs e)
{
File.Delete(_tmpFileInfo.FullName);
}
There is, of course, no 100% guarantee that the temp file is deleted, let's say if you turn the computer forcibly by powering it off. The same scenario is valid while it is copying the file.
note: what regards the event in my answer, I tried it with a console app in dot net 6, but this should be cross-compatible with a dot net core, dot net framework, etc. read more about the AppDomian events.
I left the full answer on github: https://github.com/maythamfahmi/BlogExamples/tree/main/Stackoverflow/TempFiles

Visual Studio keeps handle on folder opened during debug session

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)

Writing to a text file in AppData doesn't work - C#

I'm using the following lines of code in order to write credentials of users to a text file. It's supposed to create the directory inside AppData (which it does) but it doesn't write the credentials to the text file, it leaves it blank!
public void RegisterUserCreds()
{
string[] creds = { Username.Text, Password.Text };
string roaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Directory.Exists(roaming + "/Launcher"))
Directory.CreateDirectory(roaming + "/Launcher");
string specificFolder = roaming + "/Launcher/user_info.txt";
var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite);
var sw = new StreamWriter(fs);
sw.WriteLine(Username.Text);
fs.Close();
}
What's the problem? Thanks!
Just use the using statement when operating on streams:
public static void RegisterUserCreds()
{
string[] creds = { Username.Text, Password.Text };
string roaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Directory.Exists(roaming + "/Launcher")) Directory.CreateDirectory(roaming + "/Launcher");
string specificFolder = roaming + "/Launcher/user_info.txt";
using (var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(Username.Text);
}
}
}
In your code you were closing the file stream before the stream writer was able to flush the changes you want to write so the file was created empty.
You're closing the wrong stream. When you create new stream objects and pass an existing stream to the constructor, that new stream now "owns" the old stream. When you dispose of the newer stream it will automatically dispose of the older one.
In your situation, you're closing the "fs" stream, but the "sw" stream might not have actually written to it yet (it has it's own internal buffer). If you were to close the "sw" stream instead, it would flush it's buffer (into the "fs" stream), and then it would call fs.Dispose() for you to make sure it did the same thing.
There's a much better way, that would help you avoid doing things out-of-order like this, as well as make sure you're calling Dispose() even if exceptions get thrown (streams implement IDisposable, so you're supposed to always call their Dispose() method when you're done with them so they can internally "clean up"). The using statement is perfect for this, since it will call Dispose() even if an exception is thrown (it's a shortcut for wrapping the code with try/finally blocks):
using (var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(Username.Text);
}
}
This is the same as this:
try
{
var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite);
try
{
var sw = new StreamWriter(fs);
sw.WriteLine(Username.Text);
}
finally
{
sw.Dispose();
}
}
finally
{
fs.Dispose();
}
Even though sw.Dispose() will call fs.Dispose() for you, there is no harm in calling fs.Dispose() again. Why is it important to call Dispose()? Let's say an exception was thrown during sw.WriteLine() (e.g. out of disk space, I/O error, etc.)... the file would stay open until your app terminated. The using (or the try/catch version) would make sure the file was closed no matter what.
(side note: with streams, Dispose() and Close() do the same thing, you don't need to call both. Close() just calls Dispose() -- MS included a method called Close() because that was what people were used to with a file API, but the .NET IDisposable interface uses a method called Dispose())
(another side note: starting with .NET 4.5, many of the stream classes have an additional constructor that has a new "leaveOpen" parameter... passing true would tell that stream to NOT dispose of the original stream automatically)

Having some trouble to delete a file using FileStreams in C#

I'm writing a program that uses text files in C#.
I use a parser class as an interface between the file structure and the program.
This class contains a StreamReader, a StreamWriter and a FileStream. I use the FileStream as a common stream for the reader and the writer, else these two will conflict when both of them have the file open.
The parser class has a class variable called m_path, this is the path to the file. I've checked it extensively, and the path is correct. OpenStreams() and and ResetStreams() work perfectly, however after calling CloseStreams() in the delete() function, the program goes to the catch clause, so File.Delete(m_path) won't get executed. In other situations the CloseStreams() function works perfectly. It goes wrong when I'm trying to close the StreamReader (m_writer), but it does give an exception (File is Already Closed).
/**
* Function to close the streams.
*/
private void closeStreams() {
if (m_streamOpen) {
m_fs.Close();
m_reader.Close();
m_writer.Close(); // Goes wrong
m_streamOpen = false;
}
}
/**
* Deletes the file.
*/
public int delete() {
try {
closeStreams(); // Catch after this
File.Delete(m_path);
return 0;
}
catch { return -1; }
}
I call the function like this:
parser.delete();
Could anybody give me some tips?
Your File.Delete(m_path); will never be called, because you get an exception here:
private void closeStreams() {
if (m_streamOpen) {
m_fs.Close();
m_reader.Close();
m_writer.Close(); // throws an exception here
m_streamOpen = false;
}
}
The exception is "Cannot access a closed file"
The cause is explained in the documentation of Close() in StreamReader:
Closes the System.IO.StreamReader object and the underlying stream, and releases any system resources associated with the reader.
There are also some articles about this behaviour:
Does disposing streamreader close the stream?
Is there any way to close a StreamWriter without closing its BaseStream?
Can you keep a StreamReader from disposing the underlying stream?
Avoiding dispose of underlying stream
You should consider re-writing your code and use using() statements.
However, I experimented a bit with your code, and it worked with calling Close() in other order:
m_writer.Close();
m_reader.Close();
m_fs.Close();
However, I assume that this works only by coincidence (I used .NET 4.0 and probably this will not work in another .NET version). I would strongly advice to not do it in this way.
I tested this:
using (FileStream fs = new FileStream(m_path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
using (StreamReader reader = new StreamReader(fs))
using (StreamWriter writer = new StreamWriter(fs))
{
// so some work here
}
File.Delete(m_path);
But, I know that this may not be for you, since you may want the read and write streams available as fields in your class.
At least, you have some samples to start with ...
File.Delete should work, either you didn't call your delete method, or m_path is an invalid path

IsolatedStorageException Operation Not Permitted when calling CreateFile Repeatedly

I've read about a thousand similar posts, and have followed the general advice but am still running into the issue. Here's my scenario:
I'm working on a Windows Phone 8 app that, when the user saves, serializes all of their data into XML then uses CreateFile to store it. The problem that I'm facing is that if a user hits save several times consecutively, IsolatedStorageException:Operation Not Permitted is thrown (I'm guessing that the serialization takes long enough that the file is still being used when I attempt to access it a second time). When save is tapped the second time, is there a way for me to abort the previous action, free up the isolated storage file, and initiate the new save? Or is there a better solution?
Here's the code for my Save method (the exception occurs on the isoStore.CreateFile(filename) line):
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = isoStore.CreateFile(filename))
{
XmlSerializer xml = new XmlSerializer(GetType());
xml.Serialize(stream, this);
}
}
Any help would be amazing, as I've been stuck here for weeks.
Thanks,
Ben:
You could go with something like this.
private async Task Save(string fileName)
{
Button.IsEnabled = false;
await Task.Run(() =>
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = isoStore.CreateFile(filename))
{
XmlSerializer xml = new XmlSerializer(GetType());
xml.Serialize(stream, this);
}
}
});
Button.IsEnabled = true;
}
Why not disable the 'save' button when clicked then enable it again once the serialization completes?

Categories