File.WriteAllText fails: "file is being used by another process"

I

Ihor Bobak

Hello, everybody.

We get strange error with File.WriteAllText(), and I cannot understand why.
The stack trace is the following:

Message: The process cannot access the file
'C:\WINDOWS\TEMP\_633558601793472408_2003786287.tmp.xml' because it is being
used by another process.
Type: System.IO.IOException
Stack Trace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess
access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize,
FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean
bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess
access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamWriter.CreateFile(String path, Boolean append)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding
encoding, Int32 bufferSize)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding
encoding)
at System.IO.File.WriteAllText(String path, String contents, Encoding
encoding)
at FrameworkBase.Log.DataPortal.LogDataPortalTargetEmail.Write(ArrayList
aLogRecords)

I am on 100% sure that the file has UNIQUE name and cannot definitely be
used by any other piece of code or any other application.

Look how it works. We have a "FileTemp" class which is responsible for
generation of the temporary file names:

public class FileTemp
{
private const string _underscore = "_";

public static string Get()
{
return Get(string.Empty, ".tmp");
}
public static string Get(string aPrefix, string aExtension)
{
string path = Path.GetTempPath();
Random random = new Random();
string filename;
int count = 0;
do
{
++count;
filename = path + StringUtils.StringSuffixForce(aPrefix, "_") +
DateTime.Now.Ticks.ToString() + "_" + random.Next() +
StringUtils.StringPrefixForce(aExtension, ".");
}
while (count < 100 && File.Exists(filename));
if (count == 100)
throw new ExceptionFramework("Cannot generate filename after 100
attempts. Something impossible...", ExceptionKind.ekDeveloper);
return filename;
}
}

when it generates a name, it checks it's uniqueness. If after 100 attempts
it cannot generate it, it fails with our exception. BUT WE NEVER GOT IT
FAILED SINCE WE EXIST. And neither it failed this time.

Next, the code with File.WriteAllText is the following:

string xmlFileName = FileTemp.Get() + ".xml";
// ... here we build stringWriter - nothing to do with the file
File.WriteAllText(xmlFileName, stringWriter.ToString(), Encoding.UTF8);

As I have proven, xmlFileName has unique file name (otherwise FileTemp.Get()
would fail, but it did not).

Question: why File.WriteAllText produces System.IO.IOException "The process
cannot access the file
'C:\WINDOWS\TEMP\_633558601793472408_2003786287.tmp.xml' because it is being
used by another process." ?

..NET 2.0, Windows XP SP2, windows forms application

Please, help wiith any ideas.
 
I

Ihor Bobak

I can agree with only one thing - to return file stream instead of file name.
For this advice thank you very much.

But as to the multiple threads accessing the file - believe me, there can be
no such threads (I did not show all code, but there is a lock). As to the
file name, it WILL be unique. As to the Path.GetTempFileName() - it is buggy,
we've faced with the fact that it generates NOT unique names (this was the
reason why we made our own FileTemp).


--
Best regards,
Ihor.


Peter Duniho said:
[...]
Next, the code with File.WriteAllText is the following:

string xmlFileName = FileTemp.Get() + ".xml";
// ... here we build stringWriter - nothing to do with the file
File.WriteAllText(xmlFileName, stringWriter.ToString(), Encoding.UTF8);

As I have proven, xmlFileName has unique file name (otherwise
FileTemp.Get()
would fail, but it did not).

All due respect, you've proven nothing.

You return the filename before you've actually created and opened with
exclusive access the file, and then you proceed to do things unrelated to
the file. That gives other processes (or even your own, if you're running
multiple threads) plenty of time to create and lock a file with the same
name. Even if you didn't do all that extra work though, you can't ensure
100% lack of conflicts with the strategy you're currently using.

You can't rely on File.Exists() to tell you whether the name you generated
will be safe. Just because it returns true doesn't mean that a
millisecond later, a file that didn't exist won't all of the sudden exist.

IMHO, you would probably be better off either using a naming scheme that
doesn't rely on generating random numbers but rather instead used
something unique about the data being written that ensures unique
filenames, or use the Path.GetTempFileName() method.

Barring doing either of those things, you should fix your "FileTemp.Get()"
method so that rather than checking File.Exists(), simply tries to create
the file in question and doesn't return until it's successfully done so
(make sure you use FileMode.CreateNew to ensure that the file you've
opened is really yours). For best results, just return the FileStream for
the file, rather than the actual name.

Pete
 
J

Jeffrey Tan[MSFT]

Hi Ihor,

I agree with Peter that there is no 100% guaranteefor race condition issue.
The race condition may be pretty rare, but it is not impossible. I remember
hearing a debugging story from Mark Russinovich's blog that a file is
unexpected locked by Windows Defender application code. This type of race
condition is hard to test. Avoid it at the design&code time is the best
option.

I am not sure if this temp file is locked by the thread in your application
or another application. Can this problem be reproduced 100% or does it
occur occasionally? You may also try to use Process Explorer to search the
locking file name in all the processes at the time of the problem. This may
reveal the locking process.

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
=========================================
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 

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

Top