How do I open an already opened file with a .net StreamReader?
Asked Answered
C

5

58

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.

Cognizant answered 22/5, 2009 at 13:19 Comment(0)
H
169

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 the current attempt to open a file, 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".

Now suppose a different scenario, in which you're opening a file that isn't currently opened by any other app. FileShare.Read says "future openers can open the file only with Read 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.

Hessite answered 22/5, 2009 at 14:5 Comment(3)
What a great explanation!!! Thanks Cheeso, my project was coming down to the wire with this issue still open...but not anymore...Amygdaloid
Very useful! Created a function based on this with two consecutive try catch statements to safely open a document that is already in use in Word or Excel. Thre first attempts to open with FileShare.Read, the second with FileShare.ReadWriteTradelast
After you get the FileStream you can the use StreamReader with it. See https://mcmap.net/q/189391/-filestream-streamreader-problem-in-cDeferral
W
16

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

Woof answered 22/5, 2009 at 13:22 Comment(7)
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?Cognizant
@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.Woof
@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?Cognizant
@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 outWoof
@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 writesWoof
@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.Woof
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.Hessite
A
5

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.

Arris answered 22/5, 2009 at 13:25 Comment(1)
Not the nicest solution, but in light of any other options, I might be tempted to resort to this - thanksCognizant
R
0

Another catch is that if you open a FileStream with FileShare.ReadWrite, subsequent opens of that file must ALSO specify FileShare.ReadWrite, or you will get the 'Another process is using this file' error.

Rifkin answered 5/10, 2012 at 4:34 Comment(0)
B
-6

Using the System.Diagnostics;

You can simply call Process.Start(“filename&Path”)

Not sure if that helps but, thats what I've just used to implement a Preview PDF button on our intranet.

Blakney answered 29/11, 2012 at 13:16 Comment(1)
-1: That launches an external program to read the file and doesn't help me open it so that I can read the contents in my own programs.Cognizant

© 2022 - 2024 — McMap. All rights reserved.