Check for file in use

  • Thread starter Thread starter Brian Simmons
  • Start date Start date
B

Brian Simmons

Is there an easy way to check to see if a file is in use?

I want to open a CSV file from my .NET2 program, and write to it. However,
the file could be in use by another program. If that is the case, I don't
want to attempt to write to it, just abort.

Thanks,
Brian
 
Brian,

You could use the FileStream class and try to open the file with the
sharing/read/write options you need, and then catch the IOException (I
believe) that is thrown. However, an exception of this type can be thrown
for other conditions (like a disk error) and it's not clear (aside from the
message, which you can't reliably code against) from that exception what the
IO condition is.

Instead, what you can do is call the CreateFile API function through the
P/Invoke layer. If it returns a value of INVALID_HANDLE_VALUE, then you can
call the static GetLastWin32Error on the Marshal class and get the value
from GetLastError. This should correspond specifically to a value in
winerror.h which indicates that the file is currently in use.
 
Hello, Brian!

Put the code that opens the file into try/catch(IOException) block. In the
catch
handle assume that file is in use. You can also check IOExceptio:HResult
property as it may contain
Win32 error number that corresponds to "File is in use by another process"

Also have a look at http://www.thescripts.com/forum/thread251072.html


You wrote on Wed, 15 Aug 2007 10:59:28 -0400:

BS> I want to open a CSV file from my .NET2 program, and write to it.
BS> However, the file could be in use by another program. If that is the
BS> case, I don't want to attempt to write to it, just abort.


With best regards, Vadym Stetsiak.
Blog: http://vadmyst.blogspot.com
 
The OP can't check the HResult property because it is protected. The OP
would have to use reflection, which is a little bit too much overhead, IMO.
 
Nicolas, good answer, when i look on this quiz, i think lets try to delete
it, in case of exception so, the file is being used.

A JOKE.
--
Sincerely
Yaron Karni
http://dotnetbible.blogspot.com/


Nicholas Paldino said:
Brian,

You could use the FileStream class and try to open the file with the
sharing/read/write options you need, and then catch the IOException (I
believe) that is thrown. However, an exception of this type can be thrown
for other conditions (like a disk error) and it's not clear (aside from the
message, which you can't reliably code against) from that exception what the
IO condition is.

Instead, what you can do is call the CreateFile API function through the
P/Invoke layer. If it returns a value of INVALID_HANDLE_VALUE, then you can
call the static GetLastWin32Error on the Marshal class and get the value
from GetLastError. This should correspond specifically to a value in
winerror.h which indicates that the file is currently in use.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Brian Simmons said:
Is there an easy way to check to see if a file is in use?

I want to open a CSV file from my .NET2 program, and write to it.
However, the file could be in use by another program. If that is the
case, I don't want to attempt to write to it, just abort.

Thanks,
Brian
 
Nicholas said:
The OP can't check the HResult property because it is protected. The OP
would have to use reflection, which is a little bit too much overhead, IMO.

And using p/invoke to call CreateFile directly isn't?

IMHO, the real answer is to just catch the exception and report it to
the user if it occurs. Yes, other exceptions could occur. But they
will have unique Message properties that can be displayed to the user,
and any exception needs to be dealt with, not just the one involving a
file that's already in use.

Yes, it's difficult for the code to do something conditionally based on
the exact error, but IMHO it's not necessary for the code to do that.
Any error that prevents the file from being opened is a problem.

Pete
 
Peter Duniho said:
And using p/invoke to call CreateFile directly isn't?

Obviously, IMO, it's not.
IMHO, the real answer is to just catch the exception and report it to the
user if it occurs. Yes, other exceptions could occur. But they will have
unique Message properties that can be displayed to the user, and any
exception needs to be dealt with, not just the one involving a file that's
already in use.

I disagree here. The OP explicitly stated that they wanted to abort the
operation. Whether or not the OP wants it to abort silently, or not
silently was not specified.

If the OP wants the program to abort silently, then I would say just let
the exception fly and then wrap all the code in a try/catch statement, doing
nothing in the catch statement.

If the OP wants the program to not abort silently, then I whether or not
to propogate the exception would depend on how user-friendly the OP wants to
make his program. If the OP doesn't want something that is completely
user-friendly, then just let the exceptions fly.

However, if the does want something more user friendly message based on
a specific condition that can be checked for (in this case, is the file in
use), then working with IOException is not viable here, as it doesn't expose
the information in a way that can be easily ascertained.

The OP ^could^ use the suggestion to use the HResult property, but it
would require reflection, and at that point, you are basing the logic on an
implementation detail, which I think we would agree is a bad thing.

That leaves checking for the condition before trying to access the file,
which leaves the call to CreateFile.
Yes, it's difficult for the code to do something conditionally based on
the exact error, but IMHO it's not necessary for the code to do that. Any
error that prevents the file from being opened is a problem.

Well, whether or not it's a problem, that's for the OP (and the users of
his program) to decide, really. If he could clarify it more, that would
help.
 
There's always the remote possibility that the file won't be shared the
moment he checks it but will then be shared the moment he tries to open it
for (exclusive) writing. He therefore has to deal with the exception anyway.
Why waste the effort up front. I would personally consider a (reusable)
wrapper function that opens the file, catches the "IOException" and then
checks if it's really a sharing violation (which might also be gone by this
point so you can't entirely escape the problem - a "NumberOfRetries"
mechanism would usually solve thits however). If a sharing violation is
detected then simply throw a (custom) "SharingViolationException" with the
original "IOException" as its inner exception.
 
Larry,

That's the thing, how do you detect that the IOException actually occurs
as the result of the file being open? The two options are to use reflection
to get the HResult property which is protected and then check the error
code, or to parse the Message text, which is fragile at best (because of the
possibility of the Message property changing due to updates in the framework
and because of localization issues as well, as the Message property is more
than likely going to be localized). Neither is particularly stable, as they
both depend on implementation details.

Looking at the Marshal class, I realize that you could call the static
GetHRForException method to get the HRESULT code of the exception. This
HRESULT is the result of calling the HRESULT_FROM_WIN32 macro on the error
code ERROR_SHARING_VIOLATION (which has a value of 32):

http://msdn2.microsoft.com/en-us/library/ms680746.aspx

Looking in the code by calling the GetHRForException method, you can see
that the value 32 is definitely there (you have to convert to an unsigned
integer). Unfortunately, there is no definite way to map an HRESULT back to
a Win32 error code, as described here:

http://blogs.msdn.com/oldnewthing/archive/2006/11/03/942851.aspx

I would say checking the IOException instance in this way is slightly
better, as you are still depending on an implementation detail (in the
conversion from the HRESULT to the Win32 error code). Granted, it's highly
unlikely that you are going to get an IOException which has an error code in
the HRESULT which is from a different facility or of a different severity,
so in the end, it's up to you what your poison is.
 
That's the thing, how do you detect that the IOException actually
occurs as the result of the file being open? The two options are to use
reflection to get the HResult property which is protected and then check
the error code, or to parse the Message text, which is fragile at best
(because of the possibility of the Message property changing due to
updates in the framework and because of localization issues as well, as
the Message property is more than likely going to be localized). Neither
is particularly stable, as they both depend on implementation details.

Looking at the Marshal class, I realize that you could call the static
GetHRForException method to get the HRESULT code of the exception. This
HRESULT is the result of calling the HRESULT_FROM_WIN32 macro on the error
code ERROR_SHARING_VIOLATION (which has a value of 32):

http://msdn2.microsoft.com/en-us/library/ms680746.aspx

Looking in the code by calling the GetHRForException method, you can
see that the value 32 is definitely there (you have to convert to an
unsigned integer). Unfortunately, there is no definite way to map an
HRESULT back to a Win32 error code, as described here:

http://blogs.msdn.com/oldnewthing/archive/2006/11/03/942851.aspx

I would say checking the IOException instance in this way is slightly
better, as you are still depending on an implementation detail (in the
conversion from the HRESULT to the Win32 error code). Granted, it's
highly unlikely that you are going to get an IOException which has an
error code in the HRESULT which is from a different facility or of a
different severity, so in the end, it's up to you what your poison is.

In practice the situation doesn't normally require this level of precision.
If an "IOException" occurs and the equivalent Win32 function then indicates
a sharing violation only milliseconds later, you're pretty much guaranteed
that's what the problem is. The odds of the "IOException" being something
else are incredibly remote. Let's say it is however. Well there's still a
sharing violation at the Win32 level so reporting that is still accurate.
It's really (almost) as if you relied on Win32 directly in the first place
(for all intents and purposes). Since the course of action at this point for
most programs is to inform the user and/or log the error, you can simply
report something like:

Unable to open file "YourFile" due to a sharing violation. See application
event log for further details.

You can then clarify the situation in the log by dumping the original
"IOException" in addition to the sharing violation itself. While not a
perfect course of action theoretically, in reality it will work just fine.
In any case, I do agree with your HRESULT assessment. Not only is it an
implementation detail that you shouldn't rely on (since there's no guarantee
that ERROR_SHARING_VIOLATION will be propagated back), it seems implausible
to me that MSFT would ever recommend using "GetHRForException()" for this
purpose (mapping a standard .NET exception to a corresponding Win32 error -
in theory .NET has nothing to do with Win32).
 
Larry,

I agree with pretty much all of what you say here and agree that this
kind of targeted approach is not needed. It is because the OP specifically
asked how to check for this situation that I presented the CreateFile
approach.

I think you are suggesting going in the reverse order (catching the
exception, and then calling CreateFile). I'm suggesting that you call
CreateFile first, and then use the resulting handle to pass to a FileStream
(which is probably what is being used for file access, on some level,
ultimately). This way, you don't have to deal with an exception at all. By
getting the handle from the call to CreateFile, and then passing that to the
FileStream class, you don't have to do a "double check". The handle that
you pass to the FileStream is the one that the class uses, and you have no
gaps in your hold on the file.
 
I agree with pretty much all of what you say here and agree that this
kind of targeted approach is not needed. It is because the OP
specifically asked how to check for this situation that I presented the
CreateFile approach.

I think you are suggesting going in the reverse order (catching the
exception, and then calling CreateFile). I'm suggesting that you call
CreateFile first, and then use the resulting handle to pass to a
FileStream (which is probably what is being used for file access, on some
level, ultimately). This way, you don't have to deal with an exception at
all. By getting the handle from the call to CreateFile, and then passing
that to the FileStream class, you don't have to do a "double check". The
handle that you pass to the FileStream is the one that the class uses, and
you have no gaps in your hold on the file.

There are pros and cons to all techniques. While it ultimately boils down to
a personal judgment call, the big issue that jumps out at me with the above
suggestion is that it's usually unnecessary. Why resort to a Win32 function
like "CreateFile()" when sharing violations don't occur that frequently in
practice (though I can't speak for the op's runtime enviornment but it's
likely the same). The way I suggested defers calling it unless you have to
(infrequently). And even then it's only to check for a sharing violation (a
necessary evil), not to actually create the handle for passing to
"FileStream". While your approach might circumvent the exception, it
involves a greater mixing of Win32 and .NET IMO. It's more "in your face"
IOW and a greater support issue which is best avoided in this case (IMHO).
 
Hi Brian,

When a .CSV file is opened by Excel or other programs except Notepad, it is
opened exclusively. We could make us of this feature to check if the file
is in use. That is, try to open this file to see if there's an exception.
If yes, this file is in use; otherwise, it's not in use.

The following is a sample.

public bool IsInUse(string filename)
{
try
{
System.IO.FileStream fs =
System.IO.File.OpenWrite(filename)
fs.Close();
return false;
}
catch (Exception exp)
{
return true;
}
}

Hope this helps.
If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Linda said:
Hi Brian,

When a .CSV file is opened by Excel or other programs except Notepad, it is
opened exclusively. We could make us of this feature to check if the file
is in use. That is, try to open this file to see if there's an exception.
If yes, this file is in use; otherwise, it's not in use.

The following is a sample.

public bool IsInUse(string filename)
{
try
{
System.IO.FileStream fs =
System.IO.File.OpenWrite(filename)
fs.Close();
return false;
}
catch (Exception exp)
{
return true;
}
}

Hope this helps.
If you have any question, please feel free to let me know.

Linda, how can you possibly assume that the only exception that may
occur in the code in the try block would be from a sharing violation?

Never, ever catch an Exception like that and ignore the actual value and
assume it will be what you think it will be. This code is a bug waiting
to happen, and an incredibly difficult one to track down.

Further, the Close call should be in a finally block. Or, better, the
FileStream instantiation should be within a using statement.
 
I have to disagree with the "greater support issue part". Calling a
function through the P/Invoke layer, for the most part, is not that big of a
deal. That's a matter of opinion, and I'm willing to accept that people
have different takes on how much of a support issue, or how difficult using
the P/Invoke layer is.

In this case, however, the fundamental difference between approaches is
that yours allows for the error that can occur in the time it takes to try
and open the file and get the exception, and to call CreateFile to determine
if it is a sharing violation. With your approach, there is a window of
opportunity that introduces the possibility that the reason for the
IOException can change, whereas mine does not. If you call CreateFile
first, and it fails, you have the reason for your failure, and you can
process the error code from there (which you can end up generating a
FileLoadException for, which derives from IOException).

If the call to CreateFile succeeds, then you have the handle to the
file, and you can pass it along to the FileStream constructor, with no gaps
in your hold on the file, and eliminating the possibility of that very
unlikely, and subtle bug.
 
I should add that this is still for checking for the specific case of a
sharing violation, not as a general mechanism to open/access a file.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Nicholas Paldino said:
I have to disagree with the "greater support issue part". Calling a
function through the P/Invoke layer, for the most part, is not that big of
a deal. That's a matter of opinion, and I'm willing to accept that people
have different takes on how much of a support issue, or how difficult
using the P/Invoke layer is.

In this case, however, the fundamental difference between approaches is
that yours allows for the error that can occur in the time it takes to try
and open the file and get the exception, and to call CreateFile to
determine if it is a sharing violation. With your approach, there is a
window of opportunity that introduces the possibility that the reason for
the IOException can change, whereas mine does not. If you call CreateFile
first, and it fails, you have the reason for your failure, and you can
process the error code from there (which you can end up generating a
FileLoadException for, which derives from IOException).

If the call to CreateFile succeeds, then you have the handle to the
file, and you can pass it along to the FileStream constructor, with no
gaps in your hold on the file, and eliminating the possibility of that
very unlikely, and subtle bug.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Larry Smith said:
There are pros and cons to all techniques. While it ultimately boils down
to a personal judgment call, the big issue that jumps out at me with the
above suggestion is that it's usually unnecessary. Why resort to a Win32
function like "CreateFile()" when sharing violations don't occur that
frequently in practice (though I can't speak for the op's runtime
enviornment but it's likely the same). The way I suggested defers calling
it unless you have to (infrequently). And even then it's only to check
for a sharing violation (a necessary evil), not to actually create the
handle for passing to "FileStream". While your approach might circumvent
the exception, it involves a greater mixing of Win32 and .NET IMO. It's
more "in your face" IOW and a greater support issue which is best avoided
in this case (IMHO).
 
Additionally, the assumption that all other programs will open a CSV
file exclusively is incorrect as well.
 
[Re-posting under my MSDN email. Would really appreciate an answer.]

Date: Thu, 16 Aug 2007 07:30:43 -0400
From: GlennDoten <[email protected]>
Subject: Re: Check for file in use
Newsgroups: microsoft.public.dotnet.languages.csharp
Hi Brian,

When a .CSV file is opened by Excel or other programs except Notepad, it is
opened exclusively. We could make us of this feature to check if the file
is in use. That is, try to open this file to see if there's an exception.
If yes, this file is in use; otherwise, it's not in use.

The following is a sample.

public bool IsInUse(string filename)
{
try
{
System.IO.FileStream fs =
System.IO.File.OpenWrite(filename)
fs.Close();
return false;
}
catch (Exception exp)
{
return true;
}
}

Hope this helps.
If you have any question, please feel free to let me know.

Linda, how can you possibly assume that the only exception that may
occur in the code in the try block would be from a sharing violation?

Never, ever catch an Exception like that and ignore the actual value and
assume it will be what you think it will be. This code is a bug waiting
to happen, and an incredibly difficult one to track down.

Further, the Close call should be in a finally block. Or, better, the
FileStream instantiation should be within a using statement.
 
Hi Glenn,

Thank you for pointing out the problem within my sample code!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hi Brian,

How about the problem now?

If you need our further assistance, please feel free to let us know.

Thank you for using our MSDN Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top