File can´t rewritten because other process uses the file - c#

I´ve written some code to send files as attachments of .eml files with C#.
I create the files in an Windows temp folder and attach them to a .eml file.
The .eml files are simply opened and send through Process.Start(filename); with the standard mailing program in windows. If the users wishes to send the file again, the file will be rewritten from the database to make sure its the newest version. Now to my problem: Randomly a few times or after just the first time I rewrite the file the error appears
The process cannot access the file because it is being used by another process
I acutally did some research on the web and found this other question. So I made sure like mentioned there, that the FileStream is set correctly with all arguments.var fs = new FileStream(tempPathSave, FileMode.Open, FileAccess.Write, FileShare.ReadWrite) Has someone an idea what is the reason for my problem? My biggest problem is, that I can´t really duplicate it to say it is happening when I do this or that.
EDIT: I am using the FileStream in an using statement to make sure that it is disposed right.

It is hard to properly understand what the problem is without actually seeing the code but from my experience you are not properly disposing the filestream object so it keeps a handle to the file
var fs = new FileStream(tempPathSave, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)
So when you are done using your filestream, call
fs.Dispose();
which will dispose your filestream properly.
Moreover, you can use syntatic sugar, using which will automatically dispose your object
using(var fs = new FileStream(tempPathSave, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)){
// use fs here
// STUFF
} //at the end fs will be disposed

I´ve found a solution for my problem. I just changed the FileStream to MemoryStream because the MailMessage constructor can handle a stream, too. The only point there is, that you have to keep the MemoryStream open until the .eml file is saved. And more important you have to watch out about the memory usage und make sure the afterwards every MemoryStream will be disposed.

Related

Reading rotation log files and file locking

I have a Python service spitting out logs to text files. It rotates them every ~400KB. So the Python service opens up a handle on the file, let's call it app.log. It then writes content to the file every now and again flushes it to the disk. When it reaches a certain size, it closes it's handle, and move it to app.log.1 and starts a new handle on app.log.
So I can't change this service, but I have a C# application that will read those logs. I ran into 3 scenarios:
If I just try to read those those logs using new FileStream(path, FileMode.Open);, it won't allow me as the Python service has an handle on it.
If I try to open it using new FileStream(path, FileMode.Open, FileAccess.Read);, this allows me to read it, but if the service tries to rotate the log, it won't be able to as my C# application now has a handle on the file.
And if I try to open the file using new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Delete);, my Python service won't fail on deleting the file, but it will fail creating a new handle on app.log as the C# application would still have a handle on it.
The only solution which I'm aware of would be using Windows Shadow Copy (VSS) to create a snapshot of the logs and then read that snapshot but this would be quite expensive as we need to query the logs at every 5 minutes.
Also, I'm not interested in reading the rotated logs, app.log.1, app.log.2 etc.
Logging to text files under Windows seems to be a pain what with all the locking/handles. Does anyone have any suggestion?
You should be able to open your file as Dmitry Popov suggested in his answer as below and not affect Python writing to it, however it depends upon what locks the Python application holds on the file, it can lock you out completely and there is nothing you to do to prevent that without hacking Windows.
FileSream fs = File.Open(#"c:\Test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)
The FileStream object created in this way will still be connected to the same file after an operating system file move operation has been performed on it to rename it.
So lets assume your python app opens a file called Test.log and starts writing to it. You can read any data written to it (after python flushes its buffer) using the file stream returned from the line above. The python application can close and reopen the file as often as it wants writing each time and the reading application will remain connected to it. When the python application issues a File Move operation to rename the file to Test1.log, the file stream returned above will still be connected to the file which is now called Test1.log so you can continue reading to the end of the file before starting the new log file if that is what you want. There is one caveat to this. The Python application needs to use a Move/Rename operation rather than copying the file to a new one and deleting the old one, I'd be surprised if that is what it does though.
There is a possibility that your reading application will reach the end of the file before your writing application has finished reading from it. In this case fs.Read will keep returning 0 after a timeout until the writing application opens the file and writes some more. You can make the time out very long / infinite if you want.
As you don't want to read to the end of one file before starting the new one you could just close and reopen the file at regular intervals. The log file without the numeric suffix should always be the most recent.
If however you want your reading application to read to the end of one log file before starting at the beginning of the next one you will need to work out when the writing application has finished writing to the log file. Also it needs to find out what the file is now called so it can read n-1 next. Is there some marker written by the python application that you could look for to denote the end of a file? Does it write 'End Of Log' or something similar?
Be warned also that there are going to be short periods of time when LogFile n-1 does not exist. This is because if you have log files 0, 1, 2 and 3 it needs to make log file 3 into log file 4 before it can make log file 2 into log file 3. While it is doing this there will be a short period of time when you have log files 0, 1, 2, 4 and no 3.
Personally I would find the developer that wrote the logging for your Python application give him/her the evil eye for causing this headache in the first place. What is wrong with having the most recent log file have the largest number?
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
//Do works
}
C# thread don't lock the file in this case, your Python script can write and close the file to create another one without deadlock.
You can combine FileShare flags:
FileShare.Write | FileShare.Delete
Here's a demo:
using (var cSharp = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Write | FileShare.Delete))
{
// The Python service will be able to change and to rename the file:
using (var python = new FileStream(filename, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}
File.Move(filename, newFilename);
}
You will have to deal with concurrency. You can use FileSystemWatcher to monitor file changes.

Prevent a user from deleting, moving or renaming a file

What I am trying to do is while my program is using a file, I want to keep the user from renaming, deleting, or moving the file (well... a move is a delete and a create at a different location according to Windows FileSystemWatcher, but I digress).
It has been suggested that I use FileStream.Lock or use a Mutex. However, FileStream.Lock seems only to prevent the file from being modified which I am trying to allow. Also, I am very unsure as to if a mutex can lock a file, although I am still reading on it with in the .Net 4.0 library.
Does anyone have any advice on utilizing either one and if there is a code based solution to this problem?
When you are opening the file, you can specify the sharing mode.
Opening the file with FileAccess.Read gives you the ability to read the file, while FileShare.ReadWrite allows the file to continue to be edited, but not deleted or moved.
var fs = File.Open(#"C:\temp\file.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
MessageBox.Show("File Locked"); // While the messagebox is up, try to open or delete the file.
// Do your work here
fs.Close();
This will prevent moving or deleting the file but allows read and write:
using (FileStream fs = new FileStream(#"C:\TestDir\Test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// Do Stuff.
}
FileStream.Lock is actually a range lock which prevents modification of a particular portion of a file while the lock is held.

FileStream with locked file

I am wondering if it's possible to get a readonly FileStream to a locked file?
I now get an exception when I try to read the locked file.
using (FileStream stream = new FileStream("path", FileMode.Open))
Thanks!
You should try another constructor. They are documented at MSDN.
This one looks like a bet:
FileStream Constructor (String, FileMode, FileAccess, FileShare)
MSDN Link
FileAccess
A constant that determines how the file can be accessed by the FileStream object. This gets the CanRead and CanWrite properties of the FileStream object. CanSeek is true if path specifies a disk file.
FileShare
A constant that determines how the file will be shared by processes.
using (FileStream stream = new FileStream("path", FileMode.Open))
That will use the default value for the FileShare argument, FileShare.Read. Which denies any process from writing to the file. That cannot work if another process is writing to the file, you cannot deny a right that was already gained.
You have to specify FileShare.ReadWrite. That might still not work if the other process used FileShare.None, no workaround for that. Beware that getting read access to a file that's being written is troublesome, you don't have a reliable end-of-file indication. The last record or line in the file might have only been partially written.
I've used the following which works, however should use with caution as file can be modified while you have it open by another process.
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read,FileShare.ReadWrite);
You can simply unlock file and read file after it.
Just use Handle.exe from Sysinternals , or Unlocker with command line options.
They both can unlock file , and you can execute them from your program easily,
without leaving your program.
(But don't use them for Windows SAM file, it doesn't work with SAM ;) )
Good luck !

How to read open excel file at C#

I want to read already open excel file with C#. I am using this method but it can't read the excel file while the file is open in Microsoft excel.
FileStream stream = File.Open("myfile.xlsx", FileMode.Open, FileAccess.Read);
It gives IOException: The process cannot access the file 'myfile.xlsx' because it is being used by another process.
I hope you understands what I mean. I want to keep excel file open and while file is open at Microsoft excel i want to read it from C#. I am using C# net framework 4.0
You need to open it with FileShare.ReadWrite:
FileStream stream = File.Open("myfile.xlsx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
See this answer.
I think you can still copy the file while excel has it open, so you could make a copy of the file and then open that. Just make sure you clean up after yourself when you are done with the copy.
You could use the Interop library to use the already opened instance of Excel.
oExcel == (Excel.Application) System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application")
You can try the File.Open with a fourth parameter - fileShare.
FileStream stream = File.Open("myfile.xlsx", FileMode.Open, FileAccess.Read, FileShare.Read);
You may need to specify write access also.
To ensure that correct opening and closing of the file please look at using the c# using statements
using (FileStream stream = File.Open("myfile.xlsx", FileMode.Open, FileAccess.Read))
{
}
To open the same file more than once at the same time, it needs to be opened in shared mode.
Hope this may help others.

Exception in opening a file that is already open

I am building an application in C# in which I have to open a CSV file to read data from it. I get an exception when I try to open the CSV file from C# when that file is already open in Excel. The exception says that the process cannot access the file since it is already open. How can I solve this problem and open the file even if it is opened in other application?
Thanks,
Rakesh.
I faced this problem some time back.
You are missing the FileShare parameter. Without specifying that, if you open a file, it will be locked exclusively by your application. But since it's already been opened by Excel (or any other app), you will receive an exception.
You can try using this - I think this will be your best bet -
using (FileStream fs = File.Open(<file-path>, FileMode.Open, FileAccess.Read, FileShare.Read))
This code says: Hello Excel! If you may permit (read, not throw exception), I would like to read the file, though I will not try to own it and I know that you may modify it anytime.
If this throws error, then Excel has denied you even the read access. Too bad then!
All the best.
It is possible but you have to carefully control the file sharing you specify. Most .NET classes default to FileShare.Read, denying another process from writing to the file. But that cannot work if the file is opened by Excel, it already gained write access to it. You cannot deny a right that was already acquired.
To fix the problem, make your code look similar to this:
using (var fs = new FileStream(#"c:\\temp\\test.csv", FileMode.Open,
FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs)) {
// Read it...
}
Note the use of FileShare.ReadWrite. I verified this code works while Excel had test.csv opened.
Beware of the potential trouble you'll invite with this, odd things can happen when Excel writes to the file just as you are reading it. You'll likely read garbage, part of old data, part of new, without a good way to diagnose this.
Due to concurrency issues you can not have the option to write to two instances of the same file. It should be possible to open one as read-only this would allow for there to not be a concurrency issue as reading is guaranteed to be thread safe. This article should explain how to do what I proposed
That's not possible.
A file can be opened with different kind of protection. Excel opens the file exclusively, for the purpose of protecting the file from being changed by some other program and then reverted back when Excel saves it.
Excel could have opened the file and allowed reading, but then you could end up in a deadlock situation where two applications have the file open for reading, and neither can save anything back to it.
Another solution, suggested by this answer, is to copy the file to a temporary file and open that.
Use
System.IO.File.Copy(sourcepath, copypath, false);

Categories