When I open a file, I want to know if it is being used by another process so I can perform special handling; any other IOException I will bubble up. An IOException's Message property contains "The process cannot access the file 'foo' because it is being used by another process.", but this is unsuitable for programmatic detection. What is the safest, most robust way to detect a file being used by another process?
-
Is the type really an IOException or is it one of the types derived from IOException? – Anders Abel Apr 02 '10 at 20:01
-
1Out of interest, why do you need to detect this programatically? What do you plan to do once you have discovered that another process is using your file? – Mark Byers Apr 02 '10 at 20:02
-
@Mark Byers, see my comment against the accepted answer. – Paul Killick Apr 02 '10 at 20:56
2 Answers
This particular version of IOException
is thrown when the error code returned from the Win32 native function is ERROR_SHARING_VIOLATION
(Documentation). It has the numeric value of 0x20
but is actually stored as 0x80070020
on the HRESULT
property of the exception (it's the result of calling MakeHRFromErrorCode).
So the programatic way of checking for a sharing violation is checking the HResult
property on the IOException
for the value 0x80070020
.
public static bool IsSharingViolation(this IOException ex) {
return 0x80070020 == Marshal.GetHRForException(ex);
}
However I do question what exactly you want to do in the scenario that it was thrown as the result of a sharing violation. The moment the exception is thrown the other process could exit and hence remove the violation.

- 733,204
- 149
- 1,241
- 1,454
-
1Why not use the `ex.HResult` directly instead of `Marshal.GetHRForException`? – Anders Abel Apr 02 '10 at 20:16
-
2@Anders because the HResult property has an accessibility level of protected so it's not generally accessible. – JaredPar Apr 02 '10 at 20:18
-
1Fantastic answer JaredPar, thanks... In answer to your question; I need to detect this scenario because I need a friendly message for the presentation layer - it's ok if the conflict might not exist the moment after, due to the nature of my app... thanks again – Paul Killick Apr 02 '10 at 20:49
-
1
-
4
-
2I was getting 'Comparison to integral constant is useless; the constant is outside range of type int' and the expression evaluated to False. This worked: (uint)Marshal.GetHRForException(w) == 0x80070020 – Hari Aug 17 '17 at 10:41
-
`catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) { .. }` --- *No need* of this `0x80070020` : https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors – Angshuman Agarwal May 16 '19 at 13:53
I don't have enough "rep" to comment so hopefully this "answer" is OK...
The accepted answer is exactly what I was looking for and works perfectly, but folks here and on similar questions have questioned the utility of checking if a file is locked. It's correct that a utility function to test if a file is locked isn't much use because on the very next statement the status could have changed.
But the pattern of attempting a lock operation and then responding differently to lock errors vs. general errors is valid and useful. The most obvious thing to do is to wait for a little bit and retry the operation. This can be generalized into a helper function like this:
protected static void RetryLock(Action work) {
// Retry LOCK_MAX_RETRIES times if file is locked by another process
for (int i = 1; i <= LOCK_MAX_RETRIES; i++) {
try {
work();
return;
} catch (IOException ex) {
if (i == LOCK_MAX_RETRIES || (uint) ex.HResult != 0x80070020) {
throw;
} else {
// Min should be long enough to generally allow other process to finish
// while max should be short enough such that RETRIES * MAX isn't intolerable
Misc.SleepRandom(LOCK_MIN_SLEEP_MS, LOCK_MAX_SLEEP_MS);
}
}
}
} // RetryLock
...which can then be used like this:
public string DoSomething() {
string strReturn = null;
string strPath = @"C:\Some\File\Path.txt";
// Do some initial work...
//----------------------------------------------------------------------------------------------------
// NESTED FUNCTION to do main logic, RetryLock will retry up to N times on lock failures
Action doWork = delegate {
using (FileStream objFile = File.Open(strPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) {
// Does work here if lock succeeded, else File.Open will throw ex
strReturn = new StreamReader(objFile).ReadLine();
}
}; // delegate doWork
//----------------------------------------------------------------------------------------------------
RetryLock(doWork); // Throws original ex if non-locking related or tried max times
return strReturn;
}
...anyway, just posting in case someone with similar needs finds the pattern useful.

- 733
- 5
- 12
-
1If you are using C# 6 this is the perfect situation to use filtered exceptions `catch (IOException ex) when (i < LOCK_MAX_RETRIES && (uint) ex.HResult == 0x80070020) { Misc.SleepRandom(LOCK_MIN_SLEEP_MS, LOCK_MAX_SLEEP_MS); }` Now the exception is only caught on loops we are going to retry, if we are not going to retry it does not even make a stop in our catch block. – Scott Chamberlain Nov 04 '15 at 20:35