views:

1818

answers:

3

I have some .csv files which I'm using as part of a test bench. I can open them and read them without any problems unless I've already got the file open in Excel in which case I get an IOException:

System.IO.IOException : The process cannot access the file 'TestData.csv' because it is being used by another process.

This is a snippet from the test bench:

using (CsvReader csv = new CsvReader(new StreamReader(new FileStream(fullFilePath, FileMode.Open, FileAccess.Read)), false))
{
    // Process the file
}

Is this a limitation of StreamReader? I can open the file in other applications (Notepad++ for example) so it can't be an O/S problem. Maybe I need to use some other class? If anyone knows how I can get round this (aside from closing excel!) I'd be very grateful.

+5  A: 

EDIT

I'm still not 100% sure why this is the answer, but you can fix this problem by passing FileShare.ReadWrite to the FileStream constructor.

using (CsvReader csv = new CsvReader(new StreamReader(new FileStream(fullFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)), false)
{
  ...
}

My curiosity has a hold of me at the moment and I'm trying to understand why this is the particular answer. If I figure it out later I'll update this with the information.

The best documentation actually appears to be in the CreateFile function. This is the function .Net will call under the hood in order to open up a file (create file is a bit of a misnomer). It has better documentation for how the sharing aspect of opening a file works. Another option is to just read Cheeso's answer

JaredPar
I tried your suggestion, but it looks like excel doesn't play nicely (suprise!). Surely it must be possible to open the file though if other programs are capable of doing so?
Jon Cage
@Jon unfortunately if the other process did not open the file with the equivalent of FileShare.Read then there is no way for you to open the file until that process closes it. The way other programs are able to do this is by opening with FileShare.Read. Office is somewhat notable for opening with exclusive access and preventing other people from reading the file.
JaredPar
@Jared: Maybe I didn't make that last point clear. Notepad++ is able to open the file despite it being open already in excel but my C# test bench fails. So it *is* possible but maybe not using a StreamReader?
Jon Cage
@Jon interesting. I don't think StreamReader is the issue as it sounds like you're getting an error on the managed version of CreateFile. I'm going to play around with this for a second and see if I can figure it out
JaredPar
@Jon, updated my answer. Use FileShare.ReadWrite instead of FileShare.Read. You need to match the implicit sharing setup by the other process. In this case they are sharing reads and writes
JaredPar
@Cheeso, can you point to some documentation here? I understand the usage of FileShare from the original openers perspective but I apparently have it wrong for subsequent openers.
JaredPar
The meaning of the FileShare is the same for original and subsequent openers. It describes "constants for controlling the kind of access other FileStreams can have to the same file." If the file is already open for read+write (by Excel), then you must pass FileShare.ReadWrite to be permitted to open it. If you pass FileShare.Read, you will be blocked from opening, because someone else has it open for write. FileShare stipulates "the other guy's access", whether or not the file has been opened already.
Cheeso
+1  A: 

If another process has got a file open you can often use File.Copy and then open the copy. Not an elegant solution but a pragmatic one.

sipwiz
Not the nicest solution, but in light of any other options, I might be tempted to resort to this - thanks
Jon Cage
+23  A: 

As Jared says, You cannot do this unless the other entity which has the file open allows for shared reads. Excel allows shared reads, even for files it has open for writing. Therefore, you must open the filestream with the FileShare.ReadWrite parameter.

The FileShare param is often misunderstood. It indicates what other openers of the file can do. It applies to past as well as future openers. Think of FileShare not as a retroactive prohibition on prior openers (eg Excel), but a constraint that must not be violated with the current Open or any future Opens.

In the case of future openers, a FileShare.Read says "future openers can open the file only with Read access". In the case of past openers, FileShare.Read says "open this file for me successfully only if it any prior openers have opened it only for Read." If you specify FileShare.Read on a file that is open for writing by Excel, your open will fail, as it would violate the constraint, because Excel has it open for writing.

Because Excel has the file open for writing, you must open the file with FileShare.ReadWrite if you want your open to succeed. Another way to think of the FileShare param: it specifies "the other guy's file access".

Logically, these semantics make sense - FileShare.Read means, you don't want to read the file if the other guy is already writing it, and you don't want the other guy to write the file if you are already reading it. FileShare.ReadWrite means, you are willing to read the file even if the another guy is writing it, and you have no problem letting another opener write the file while you are reading it.

In no case does this permit multiple writers. FileShare is similar to a database IsolationLevel. Your desired setting here depends on the "consistency" guarantees you require.

Example:

using (Stream s = new FileStream(fullFilePath, 
                                 FileMode.Open,
                                 FileAccess.Read,
                                 FileShare.ReadWrite))
{
  ...
}

or,

using (Stream s = System.IO.File.Open(fullFilePath, 
                                      FileMode.Open, 
                                      FileAccess.Read, 
                                      FileShare.ReadWrite))
{
}

Addendum:

The documentation on System.IO.FileShare is a little slim. If you want to get the straight facts, go to the documentation for the Win32 CreateFile function, which explains the FileShare concept better.

Cheeso
Excellent - that fixed it and thanks for the explanation :-)
Jon Cage
What a great explanation!!! Thanks Cheeso, my project was coming down to the wire with this issue still open...but not anymore...
w4ik