I face weird problem with IOException on different servers. Here is my story:
I have win server (C#) that monitors folder for file changes. Also, a FTP server (WinSSHD) is pointed on same folder. When someone uploads a file WinService checks is write complete or not. If file is uploaded WinService renames it via File.Move method.
Is write complete check is made by File.Open method. Like this:
public static bool IsWriteComplete(string filePath)
{
bool isWriteComplete;
FileStream stream = null;
try
{
stream = File.Open(filePath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite|FileShare.Delete);
isWriteComplete = true;
}
catch (IOException)
{
isWriteComplete = false;
}
catch (Exception ex)
{
isWriteComplete = false;
}
finally
{
if (stream != null) { stream.Close(); }
}
return isWriteComplete;
}
For some reason on one server it works perfectly (Win2003SP2) and on another one (Win2003SP1) is not working at all. The IsWriteComplete method says True, WinServer tries to move file and dies by IOException later. I haven't found any references regarding such issue.
To fix the issue I replaced File.Open by File.Move.
Before:
stream = File.Open(filePath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite|FileShare.Delete);
After:
File.Move(filePath, filePath);
And now it works on second server (Win2003SP1) and doesn't work on first one (Win2003SP2).
Does anybody know why it works as such?
When you use File.Open you have a "race condition", even if you use FileShare.None - after you close the file, another thread/process can sneak in and lock the file (open it) before you call File.Move. This other process might not be the FTP server, but could be something you are not even aware of, such as a virus checker. Thus you should use File.Move instead of File.Open, and catch the IOException if the File.Move fails, and then retry, until it succeeds.
When you use FileShare.ReadWrite|FileShare.Delete, you allow other processes to read, write or delete the file while you're opening it. On the other side, you can open the file while others also access it. So, try FileShare.None to open the file exclusively, which should fail as long as the file is in use by the FTP server.
Also, you should use FileAccess.ReadWrite to make sure that you have full access to the file. You do not know how the FTP server locks the file, maybe it allows other processes to open the file in read-mode and declines write mode while the upload is still in process. So, FileAccess.ReadWrite is better.
MSDN reference: http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx and http://msdn.microsoft.com/en-us/library/system.io.fileaccess.aspx
About your code:
catch (IOException)
{
isWriteComplete = false;
}
catch (Exception ex)
{
isWriteComplete = false;
}
The catch block for IOException is not neccessary if you also catch Exception...
Alternatively, you can check in other way then open method.
FileInfo fileInfo = new FileInfo(filePath);
bool isNormal = ((fileInfo.Attributes & FileAttributes.Normal) == FileAttributes.Normal);
//or
bool isReadOnly = ((fileInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly);
You can find the method in either way in order to understand whether file is available or not. And you can continue based upon the status.
Please let me know if you are looking for some other things.
Found the root cause of the behavior.
There was different FTP service installed: Techia. For some reason Tectia doesn't set write lock on uploading files. That's why File.Open(filePath, FileAccess.Write) doesn't throw IOException.
Related
I have the following code to detect if a file is already in use or not:
public static bool FileInUse(string sFile)
{
FileStream file = null;
try
{
file = new FileStream(sFile, FileMode.Open, FileAccess.ReadWrite);
file.Close();
file = File.OpenWrite(sFile);
file.Close();
return false;
}
catch (Exception)
{
try { file.Close(); }
catch (Exception) { }
return true;
}
}
When the file is in use then, new FileStream(sFile, FileMode.Open, FileAccess.ReadWrite) does not throw error but File.OpenWrite(sFile) throws error:
The process cannot access the file because it is being used by another
process.
Why is that? Because I want to check if a file can be opened in write mode.
The constructor you are using for FileStream defaults to sharing mode FileShare.Read, meaning that later requests to open the file for reading (but not writing) will be allowed. This will work even when the file is already open for reading (if that is not exclusive reading).
On File.OpenWrite, MSDN online states:
This method is equivalent to the FileStream(String, FileMode, FileAccess, FileShare) constructor overload with file mode set to OpenOrCreate, the access set to Write, and the share mode set to None.
http://msdn.microsoft.com/en-us/library/system.io.file.openwrite(v=vs.110).aspx
This means that File.OpenWrite tries to get exclusive Read access to the file, not only exclusive Write access.
It sounds like when you test your file for "in use" it is open for reading.
Both methods check that the file is available for exclusive writing. You need to decide if your application also requires exclusive reading.
Because
File.OpenWrite(sFile) -> return new FileStream(path, FileMode.OpenOrCreate,
FileAccess.Write, FileShare.None);
but
public FileStream(String path, FileMode mode, FileAccess access)
: this(path, mode, access, FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) {
}
As you can see the first one uses FileShare.None, but second uses FileShare.Read
From MSDN:
FileShare.None Declines sharing of the current file. Any request to open the file (by this process or another process) will fail until the file is closed.
FileShare.Read Allows subsequent opening of the file for reading. If this flag is not specified, any request to open the file for reading (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.
A little off-topic, but: there is no very good way to check if file is in use or no, because in the fraction of time between your call to the FileInUse method and your call to open file, this file may get locked again. This FileInUse method is ultimately redundant. You can just catch exceptions when your try your exclusive access to file.
I need to verify if specific file is open to prevent the copy of that file.
I try lot of examples but any doesn't work! I try, for example, this:
protected virtual bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
I need orientation... where am I fail? suggestions?
If you want to know whether your application already had the file open, you should just save the FileStream in a field, and reset the field to null when you close the stream. Then you can simply test and get the FileStream of the file.
If you want to know whether another application already has the file open, then there is not much you can do. You'll might get an exception when you try to open the file. But even if you knew, then you wouldn't be able to prevent the copy of that file, because you don't have a reference to the file or its FileStream in your application.
You can suffer from a thread race condition on this which there are documented examples of this being used as a security vulnerability. If you check that the file is available, but then try and use it you could throw at that point, which a malicious user could use to force and exploit in your code.
Your best bet is a try catch / finally which tries to get the file handle.
try
{
using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
{
// File/Stream manipulating code here
}
} catch {
//check here why it failed and ask user to retry if the file is in use.
}
or
see this another options
https://stackoverflow.com/a/11060322/2218635
I am struggling with this for some time now. I can not access a file after I call File.Copy method. Here is what I tried:
File.Copy(sourceFile, destinationFile, true);
FileStream fs = File.Open(destinationFile, FileMode.Open);
I am getting UnauthorizedAccessException on the second line. It says: Access to the path ... is denied. I have also tried suggestion stated here but that didn't work.
Any help is appreciated.
EDIT: Here is what I found out. If I do this:
File.Copy(sourceFile, destinationFile, true);
FileStream fs = File.Open(destinationFile, FileMode.Open, FileAccess.Read);
It doesn't throw exception. The file I am trying to access is read only. So, I tried to remove read only attribute like this:
File.Copy(sourceFile, destinationFile, true);
FileInfo fileInfo = new FileInfo(destinationFile);
fileInfo.IsReadOnly = false;
FileStream fs = File.Open(destinationFile, FileMode.Open, FileAccess.ReadWrite);
And I get the same exception as before. By the way, I checked if I can open file manually and edit it, and I can. Of course, when I uncheck read only check box. I have also checked file attributes in windows explorer while debugging, right after the third line, and the file is no longer read only. Having all that in checked, I don't understand why is the exception being thrown on the fourth line.
Are you sure it's ONLY files copied using File.Copy that you can't open, and not every file in the target folder? And is this a regular NTFS folder, or network share?
If you are running an antivirus or security software, try disabling it. After a file is created they will often open a file to scan it.
Update
According to http://msdn.microsoft.com/en-us/library/b9skfh7s.aspx - UnauthorizedAccessException thrown by File.Open will not give the message 'Access to the path... is denied'. (edit: I can't see that message for File.Copy either, so this might be wrong)
I suspect it is your File.Copy that fails with that exception, and you don't have rights to read the source file, or write the target file. You're probably looking at the highlighted source code line, which shows the next line to be exectued.
So - maybe your copy fails, not the File.Open ?
Try this!
private bool CheckFileHasCopied(string FilePath)
{
try
{
if (File.Exists(FilePath))
{
using (File.OpenRead(FilePath))
{
return true;
}
}
else
{
return false;
}
}
catch (Exception)
{
Thread.Sleep(2000);
return CheckFileHasCopied(FilePath);
}
}
if (CheckFileHasCopied(destinationFile)) { File.Delete(sourceFile); }
i have written some pdf files to a temp directory and these get displayed as a thumbnail that the user can view. when i close my form i clean up all of the files in the temp directory.
If however the user has one of the thumbnails open and then closes my application - it deletes the files and then throws an exception because the pdf is open in another process and cant be cleaned up.
I guess this is a shocking programming decision by me, but i am still a novice! How should i account for this in my code?
Thanks
You can detect if the file is in use by using code similar to below, then use that to warn the user that a file can't be deleted.
Unfortunately you can't delete a file that is in use.
public static bool IsFileInUse(string pathToFile)
{
if (!System.IO.File.Exists(pathToFile))
{
// File doesn't exist, so we know it's not in use.
return false;
}
bool inUse = false;
System.IO.FileStream fs;
try
{
fs = System.IO.File.Open(pathToFile, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Read, System.IO.FileShare.None);
fs.Close();
}
catch (System.IO.IOException ex)
{
string exMess = ex.Message;
inUse = true;
}
return inUse;
}
You should catch that exception (in catch block you can inform user to close that file or it will not be deleted), and if the temp directory is yours you can try to delete it when application starts (or when it ends again), if its windows temp directory, then it does not matter that much
Tools like File Unlocker can release a file. However I think this could make programs depending on the file crash...
Maybe you can look up how they unlock files or manage to execute the unlocker via Process.Start to unlock your file and delete it.
However if it's you blocking the file you should try and fix this in your programm. Maybe you should dispose all loaded files (filestreams etc) before trying to clean it up.
If I am working with files in C#, what is the best way to check if a file is locked in C#?
If I use File.OpenWrite(), which returns a FileStream, and this returns CanWrite = false, is this a way of checking so?
Also, what is it wise to write to just one location in my code? So I specify a path to write to like "TestExample.txt", but then if I run my code, successfully write to the file, run again, the same data will be in there. Is it possible to overwrite the data already in a file altogether? Or do I need to manually delete (I know this sounds naive).
Thanks
File.OpenWrite will throw an IOException if the file is locked, so calling that method and then checking the CanWrite property would not be a good approach. You will need a try-catch handling the IOException.
If you want to replace the contents of your file, you can simply delete it before writing to it (if it exists):
try
{
if (File.Exists(fileName))
{
File.Delete(fileName);
}
using (FileStream stream = File.OpenWrite(fileName))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write("your content");
}
}
catch (IOException)
{
// the file is locked
}