c# Unable to open file for reading - c#

I'm writing a program that uses FileSystemWatcher to monitor changes to a given directory, and when it receives OnCreated or OnChanged event, it copies those created/changed files to a specified directories. At first I had problems with the fact that OnChanged/OnCreated events can be sent twice (not acceptable in case it needed to process 500MB file) but I made a way around this and with what I'm REALLY BLOCKED with is getting the following IOException:
The process cannot access the file 'C:\Where are Photos\bookmarks (11).html' because it is being used by another process.
Thus, preventing the program from copying all the files it should.
So as I mentioned, when user uses this program he/she specifes monitored directory, when user copies/creates/changes file in that directory, program should get OnCreated/OnChanged event and then copy that file to few other directories.
Above error happens in all cases, if user copies few files that needs to overwrite other ones in folder being monitored or when copying bulk of several files or even sometimes when copying one file in a monitored directory.
Whole program is quite big so I'm sending the most important parts.
OnCreated:
private void OnCreated(object source, FileSystemEventArgs e) {
AddLogEntry(e.FullPath, "created", "");
// Update last access data if it's file so the same file doesn't
// get processed twice because of sending another event.
if (fileType(e.FullPath) == 2) {
lastPath = e.FullPath;
lastTime = DateTime.Now;
}
// serves no purpose now, it will be remove soon
string fileName = GetFileName(e.FullPath);
// copies file from source to few other directories
Copy(e.FullPath, fileName);
Console.WriteLine("OnCreated: " + e.FullPath);
}
OnChanged:
private void OnChanged(object source, FileSystemEventArgs e) {
// is it directory
if (fileType(e.FullPath) == 1)
return; // don't mind directory changes itself
// Only if enough time has passed or if it's some other file
// because two events can be generated
int timeDiff = ((TimeSpan)(DateTime.Now - lastTime)).Seconds;
if ((timeDiff < minSecsDiff) && (e.FullPath.Equals(lastPath))) {
Console.WriteLine("-- skipped -- {0}, timediff: {1}", e.FullPath, timeDiff);
return;
}
// Update last access data for above to work
lastPath = e.FullPath;
lastTime = DateTime.Now;
// Only if size is changed, the rest will handle other handlers
if (e.ChangeType == WatcherChangeTypes.Changed) {
AddLogEntry(e.FullPath, "changed", "");
string fileName = GetFileName(e.FullPath);
Copy(e.FullPath, fileName);
Console.WriteLine("OnChanged: " + e.FullPath);
}
}
fileType:
private int fileType(string path) {
if (Directory.Exists(path))
return 1; // directory
else if (File.Exists(path))
return 2; // file
else
return 0;
}
Copy:
private void Copy(string srcPath, string fileName) {
foreach (string dstDirectoy in paths) {
string eventType = "copied";
string error = "noerror";
string path = "";
string dirPortion = "";
// in case directory needs to be made
if (srcPath.Length > fsw.Path.Length) {
path = srcPath.Substring(fsw.Path.Length,
srcPath.Length - fsw.Path.Length);
int pos = path.LastIndexOf('\\');
if (pos != -1)
dirPortion = path.Substring(0, pos);
}
if (fileType(srcPath) == 1) {
try {
Directory.CreateDirectory(dstDirectoy + path);
//Directory.CreateDirectory(dstDirectoy + fileName);
eventType = "created";
} catch (IOException e) {
eventType = "error";
error = e.Message;
}
} else {
try {
if (!overwriteFile && File.Exists(dstDirectoy + path))
continue;
// create new dir anyway even if it exists just to be sure
Directory.CreateDirectory(dstDirectoy + dirPortion);
// copy file from where event occured to all specified directories
using (FileStream fsin = new FileStream(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (FileStream fsout = new FileStream(dstDirectoy + path, FileMode.Create, FileAccess.Write)) {
byte[] buffer = new byte[32768];
int bytesRead = -1;
while ((bytesRead = fsin.Read(buffer, 0, buffer.Length)) > 0)
fsout.Write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
if ((e is IOException) && (overwriteFile == false)) {
eventType = "skipped";
} else {
eventType = "error";
error = e.Message;
// attempt to find and kill the process locking the file.
// failed, miserably
System.Diagnostics.Process tool = new System.Diagnostics.Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = "\"" + srcPath + "\"";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern)) {
System.Diagnostics.Process.GetProcessById(int.Parse(match.Value)).Kill();
}
Console.WriteLine("ERROR: {0}: [ {1} ]", e.Message, srcPath);
}
}
}
AddLogEntry(dstDirectoy + path, eventType, error);
}
}
I checked everywhere in my program and whenever I use some file I use it in using block so even writing event to log (class for what I omitted since there is probably too much code already in post) wont lock the file, that is it shouldn't since all operations are using using statement block.
I simply have no clue who's locking the file if not my program "copy" process from user through Windows or something else.
Right now I have two possible "solutions" (I can't say they are clean solutions since they are hacks and as such not desirable). Since probably the problem is with fileType method (what else could lock the file?) I tried changing it to this, to simulate "blocking-until-ready-to-open" operation:
fileType:
private int fileType(string path) {
FileStream fs = null;
int ret = 0;
bool run = true;
if (Directory.Exists(path))
ret = 1;
else {
while (run) {
try {
fs = new FileStream(path, FileMode.Open);
ret = 2;
run = false;
} catch (IOException) {
} finally {
if (fs != null) {
fs.Close();
fs.Dispose();
}
}
}
}
return ret;
}
This is working as much as I could tell (test), but... it's hack, not to mention other deficients.
The other "solution" I could try (I didn't test it yet) is using GC.Collect() somewhere at the end of fileType() method. Maybe even worse "solution" than previous one.
Can someone pleas tell me, what on earth is locking the file, preventing it from opening and how can I fix that? What am I missing to see?
Thanks in advance.

The problem is most likely that the file is still being copied while you already try to access it. This can happen especially on large files.
You can try to check whether the file can be opened with write permissions before you actually start your processing. For details how to do that check here.
If you can influence the process creating the file there might be a better solution. First copy the file with a temporary extension, and then, after the copying is completed, rename it so that the FileSystemWatcher event will be triggered.

You can try with Volume Shadow Copies.
See www.codeproject.com/KB/dotnet/makeshadowcopy.aspx for more details.

FileSystemWatcher events trigger when the file begins the copy, not at the end, so it's common to run into this kind of errors.
Your first approach will work, however, I would recommend spinning the I/O intensive code on another thread, and using an incremental Sleep() instead of the busy waiting you do.
However, if you have access to the software that actually creates the files, the extension changing is a slightly less complicated solution. Just beware, that a xls filter on the FileSystemwatcher will match a file called myfile1.xls.temp, as I found that out the hard way :)

Related

FileSystemWacher is locking some files

I am using this code to monitor creation of files in certain folder:
_watcher = new RecoveringFileSystemWatcher(SourceFolder, "*.xml");
_watcher.Created += (_, e) =>
{
ProcessFile(e.Name);
};
RecoveringFileSystemWatcher is a fileSystemWatcher wrapper. It's constructor is:
public RecoveringFileSystemWatcher (string path, string filter)
{
_containedFSW = new FileSystemWatcher(path, filter);
}
The process works as expected but for some files, randomly, an exception is thrown telling that the file is used by another process.
This is the method that is launched upon file creation:
var nfo = new FileInfo(filePath);
if (nfo.Exists)
{
var archivoXml = nfo.Name;
string archivo = String.Empty;
try
{
string content = Task.Run(async () => await GetFileContent(filePath)).Result;
if (String.IsNullOrEmpty(content))
return false;
XmlDocument xml = new XmlDocument();
xml.LoadXml(content);
//The rest of the method
}
}
the method GetFileContent is this:
private async Task<string> GetFileContent(string filePath)
{
string content = String.Empty;
try
{
Console.Write("ONE - "); InfoLog.Save($"ONE {filePath}");
using (StreamReader sr = new StreamReader(filePath))
{
Console.Write("TWO - "); InfoLog.Save($"TWO {filePath}");
content = await sr.ReadToEndAsync().ConfigureAwait(false);
Console.Write($"THREE {(sr.BaseStream == null ? "Closed" : "Opened")} - "); InfoLog.Save($"THREE {(sr.BaseStream == null ? "Closed" : "Opened")} {filePath}");
sr.Close();
Console.WriteLine($"FOUR {(sr.BaseStream == null ? "Closed" : "Opened")}"); InfoLog.Save($"FOUR {(sr.BaseStream == null ? "Closed" : "Opened")} {filePath}");
}
}
catch (Exception ex)
{
InfoLog.Save($"XML file could be read -> {filePath}. See error log.");
ErrorLog.Save(ex);
}
return content;
}
Look at the log information I am writing to debug the process.
I got one case with a file called 1112186.xml.... this is recorded in the log:
18/12/2018 19:12:10 ONE D:\GestorDocumental\Origen\1112186.xml
18/12/2018 19:12:10 XML file could not be read -> D:\GestorDocumental\Origen\1112186.xml. See error log.
As you see, the exception is thrown at the "using" instruction.
If I see the full log, I can see that file, 1112186.xml, is never used before, so the only chance is that FSW keeps the file opened. I don't know why, but it seems this is happening.
It is clear also that this process is locking the file, because when I exit the console application and then run again, the file can be processed.
Any help about this, please?
thanks
Jaime
I usually use this method to check if file is locked. I got it from one of the link in stackoverflow.
public static bool IsFileClosed(string filepath)
{
bool fileClosed = false;
int retries = 20;
const int delay = 400; // set a delay period = retries*delay milliseconds
if (!File.Exists(filepath))
return false;
do
{
try
{
// Attempts to open then close the file in RW mode, denying other users to place any locks.
FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
fs.Close();
fileClosed = true; // success
}
catch (IOException) { }
retries--;
if (!fileClosed)
Thread.Sleep(delay);
}
while (!fileClosed && retries > 0);
return fileClosed;
}
This is a new class called FileTimerWatcher (it will have logger injected):
public FileTimerWatcher(ILogger logger) : base(logger)
{
if (timer == null)
{
// Create a timer with a 1.5 second interval.
// monitor the files after 1.5 seconds.
timer = new Timer(delay);
// Hook up the event handler for the Elapsed event.
timer.Elapsed += new ElapsedEventHandler(ProcessFolder);
timer.AutoReset = true;
timer.Enabled = true;
}
}
private void ProcessFolder(object sender, ElapsedEventArgs e)
{
var LastChecked = DateTime.Now;
string[] files = System.IO.Directory.GetFiles(SourceDirectory, somefilter, System.IO.SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
ProcessFile(file); // process file here
}
}

Deploying C# Solution with Native Dll into single EXE

I have A C# Visual Studio 2012 Solution that relies on a native dll that I use PInvoke to access. When I deploy the app I will have to ensure that this Dll is in the app folder.
Is there anyway I can merge this Dll into the executable?
perhaps as a resource?
I have heard of ILMerge but I am told it cant cope with native code.
Any help would be appreciated.
You can create a Setup package project with Visual Studio that deploys all your files to the correct location or use other third party packaging software (like full InstallShield or alternatives)
However, your question reminds me on the Open Hardware Monitor project where they include drivers as embedded resource and extract them when the user starts the application. It works like this: they've added WinRing0.sys and WinRing0x64.sys to the project and set their Build Action to Embedded Resource, then they have a method that extracts the driver from the resource:
private static bool ExtractDriver(string fileName) {
string resourceName = "OpenHardwareMonitor.Hardware." +
(OperatingSystem.Is64BitOperatingSystem() ? "WinRing0x64.sys" :
"WinRing0.sys");
string[] names =
Assembly.GetExecutingAssembly().GetManifestResourceNames();
byte[] buffer = null;
for (int i = 0; i < names.Length; i++) {
if (names[i].Replace('\\', '.') == resourceName) {
using (Stream stream = Assembly.GetExecutingAssembly().
GetManifestResourceStream(names[i]))
{
buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
}
}
}
if (buffer == null)
return false;
try {
using (FileStream target = new FileStream(fileName, FileMode.Create)) {
target.Write(buffer, 0, buffer.Length);
target.Flush();
}
} catch (IOException) {
// for example there is not enough space on the disk
return false;
}
// make sure the file is actually writen to the file system
for (int i = 0; i < 20; i++) {
try {
if (File.Exists(fileName) &&
new FileInfo(fileName).Length == buffer.Length)
{
return true;
}
Thread.Sleep(100);
} catch (IOException) {
Thread.Sleep(10);
}
}
// file still has not the right size, something is wrong
return false;
}
They're reading the resource into a buffer, write that buffer to disk and wait until the file has been flushed to disk.
My solution is conceptually similar to the one presented by Wouter.
It's what we use in our own app, and we can use native/mixed-mode and c# dlls all embedded in the same .exe.
It extracts the dlls into a temp dir everytime the application is run. Obviously you might not want to do this in the production version, where the dlls will be stable; you might choose a different directory there (probably somewhere in %AppData%). It will use an existing dll with the same version number, though (e.g. it's only done the first time when opening the app multiple times between booting the computer).
Since we're doing
AppDomain.CurrentDomain.AssemblyResolve += (sender, args)
this function is getting called wherever the system tries to resolve a dll. And since it's initalised in the static Program class, it all works automagically.
Program.cs:
namespace MyApp
{
internal class Program
{
static Program()
{
LoadAssemblyResource.Initialize("MyApp");
}
//....
}
}
LoadAssemblyResource.cs
namespace MyAppStartup
{
public static class LoadAssemblyResource
{
private readonly static String _version_string =
Assembly.GetExecutingAssembly().GetName().Version.ToString();
private readonly static String _dll_path = Path.GetTempPath()
+ "\\MyApp\\" + _version_string;
static public String last_error_msg = null;
public static bool WriteBytesToFile(string filename, byte[] bytes)
{
try
{
var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
return true;
}
catch (Exception e)
{
Console.WriteLine("Writing file failed. Exception: {0}", e.ToString());
}
return false;
}
public static Assembly LoadUnsafe(String assembly_name, Byte[] assembly)
{
if (!Directory.Exists(_dll_path))
{
Directory.CreateDirectory(_dll_path);
Console.WriteLine("Created tmp path '" + _dll_path + "'.");
}
String fullpath = _dll_path + "\\" + assembly_name;
if (!File.Exists(fullpath))
{
Console.WriteLine("Assembly location: " + fullpath + ".");
if (!WriteBytesToFile(fullpath, assembly))
return null;
}
return Assembly.UnsafeLoadFrom(fullpath);
}
public static void Initialize(String exe_name)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String assembly_name = new AssemblyName(args.Name).Name + ".dll";
String resource_name = exe_name + "." + assembly_name;
using (var stream =
Assembly.GetExecutingAssembly().GetManifestResourceStream(resource_name))
{
if (stream == null)
return null;
Byte[] assembly_data = new Byte[stream.Length];
stream.Read(assembly_data, 0, assembly_data.Length);
try
{
Assembly il_assembly = Assembly.Load(assembly_data);
return il_assembly;
}
catch (System.IO.FileLoadException ex)
{
// might have failed because it's an mixed-mode dll.
last_error_msg = ex.Message;
}
Assembly mixed_mode_assembly = LoadUnsafe(assembly_name, assembly_data);
return mixed_mode_assembly;
}
};
}
}
}

Creating a queue to handle jobs triggered by FileSystemWatcher

I have built a small tray app that will watch a folder and when a new file is added it runs a job. The job is to watch for video files and convert them to .mp4 using handBrakeCli. I have all this logic worked out. The problem I run into is that if there is more than one file I want it to queue the job til the prior one is complete. I am fairly new to c# and I am not sure of the best way to handle this.
one idea is to create a queue somehow, a file to store the commands in order maybe, then execute the next one after the process is complete. We are dealing with large movie files here so it can take a while. I am doing this on a quad core with 8gb of RAM and it seems to generally take about 30mins to complete a full length movie. I just dont know how to do this.
here is the code I have so far. there are some bits in here that are for future functionality so it refers to some classes that you wont see but it doesnt matter as they aren't used here. any suggestions are welcome.
public void Watcher()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = textBox1.Text + "\\"; //path to watch
watcher.Filter = strfilter; //what types to look for set to * and i will filter later as it cant accept an array
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; //properties to look at
watcher.IncludeSubdirectories = true; //scan subdirs
watcher.Created += new FileSystemEventHandler(OnChanged);
//TODO: make this only run if the files are of a certain type
watcher.EnableRaisingEvents = true; // start the watcher
}
static 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;
}
// Define the event handlers.
private void OnChanged(object source, FileSystemEventArgs e)
{
string sFile = e.FullPath;
//check that file is available
FileInfo fileInfo = new FileInfo(sFile);
while (IsFileLocked(fileInfo))
{
Thread.Sleep(500);
}
if (System.Diagnostics.Process.GetProcessesByName("HandBrakeCLI").Length != 0)
{
Thread.Sleep(500);
}
else
{
//hbOptions hbCl = new hbOptions();
//hbCli = hbCl.HbCliOptions();
if (textBox3.Text != "")
{
hbCli = textBox3.Text.ToString();
}
else
{
hbCli = "-e x264 -q 20 -B 160";
}
string t = e.Name;
string s = t.Substring(0, t.Length - 4); //TODO: fix this its not reliable
file = e.FullPath;
string opath = textBox1.Text.ToString();
cmd = "-i \"" + file + "\" -o \"" + opath + "\\" + s + ".mp4\" " + hbCli;
try
{
for (int i = 0; i < Ext.Count(); i++)
{
if (e.Name.Contains(Ext[i]))
{
Process hb = new Process();
hb.StartInfo.FileName = "D:\\Apps\\Handbrake\\Install\\Handbrake\\HandBrakeCLI.exe";
hb.StartInfo.Arguments = cmd;
notifyIcon.BalloonTipTitle = "Now Converting";
notifyIcon.BalloonTipText = file;
notifyIcon.ShowBalloonTip(2000);
hb.Start();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void button1_Click(object sender, EventArgs e) //ok button
{
//add each array item to the list
for (int i = 0; i < filter.Count(); i++)
{
Ext.Add(filter[i]);
}
if (textBox1.Text != "" && textBox1.Text.Length > 2)
{
Watcher(); //call watcher to run
}
this.WindowState = FormWindowState.Minimized;
}
}
You way want to utilize WCF and MsmqQueueBinding:
Service uses NetMsmqBinding
Queue implemented for you using built-in into Windows queue called MSMQ (you can use MMC snap-it to control main, dead and poisoned letters queue. Both client and server Windows OS are bundled with it, turn it on in Windows Features)
Client puts process request into the queue and forgets about it
Service receives it automatically and process
Queue is durable, persisted and transactional (if you want)
You can run a queue on the same machine or on another intranet server
See the follow wonderful tutorial:
MSMQ, WCF and IIS: Getting them to play nice (Part 1)
MSMQ, WCF and IIS: Getting them to play nice (Part 2)
MSMQ, WCF and IIS: Getting them to play nice (Part 3)

Is file being copied right now? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# - from time to time check if a file exists and read from it
I'm using file watcher to check if a file was craeted in a directory. If it was, then I want to open it and remove to another directory.
My approach is also to use FileShare.None to an ensure exclusive access. What I did is:
class Program
{
private static void Main(string[] args)
{
FileSystemWatcher fileWatcher = new FileSystemWatcher();
fileWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
fileWatcher.Created += fileWatcher_Created;
fileWatcher.EnableRaisingEvents = true;
Console.ReadLine();
}
private static void fileWatcher_Created(object sender, FileSystemEventArgs e)
{
WorkOnFile(e.FullPath);
}
//must be done completely. How do I ensure it?
private static void WorkOnFile(string fileName)
{
using (FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
Thread.Sleep(40000); // some long operations
}
}
}
The bottom line is, that it necessary to do WorkOnFile() completely and only one time for one file. If a file being copied, then I need to call WorkOnFile() again until it has completely been copied and none process is using it.
How do I reach it?
If you need that file moved as fast as possible, you could try using FileWatcher to monitor 'LastWrite' and after a small delay, trying to move the file.
I had to do something very similar, but there was no hurry, just important that the file was moved reliably. So I instead used a System.Timers.Timer to run once a minute:
var scanDirectoryIn = new DirectoryInfo(folderIn);
foreach (var fileInfo in scanDirectoryIn.GetFiles())
{
if (fileInfo.Extension != ".csv") continue;
if (DateTime.UtcNow.Subtract(fileInfo.LastWriteTimeUtc).TotalMinutes < 5) continue;
try
{
fileInfo.MoveTo(folderOut + "\\" + fileInfo.Name);
}
catch (Exception) {}
}
//must be done completely. How do I ensure it?
private static void WorkOnFile(string fileName)
{
while(true){
try{
using (FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
Thread.Sleep(40000); // some long operations
break; //exit while() infinite loop
}
}
catch(Exception e){
//file is locked because being written. wait a few seconds then retry
Thread.Sleep(10000);
}
}
}

c# How to access file if that file is processed by other process?

public static void WriteLine(string text)
{
StreamWriter log;
if (!File.Exists(Filename))
{
log = new StreamWriter(Filename);
}
else
{
log = File.AppendText(Filename);
}
while this method is processed, other process also call this method. There will be error occur "file has been acess by other process". How to solve this problem by waiting the previous process finish.
I think the op wants to wait until the filehandle is free to use and then write to the file. In this case you should try to get the filehandle, catch the exception and if the exception is because the file is accessed by another process then wait a short time and try again.
public static void WriteLine(string text)
{
bool success = false;
while (!success)
{
try
{
using (var fs = new FileStream(Filename, FileMode.Append))
{
// todo: write to stream here
success = true;
}
}
catch (IOException)
{
int errno = Marshal.GetLastWin32Error();
if(errno != 32) // ERROR_SHARING_VIOLATION
{
// we only want to handle the
// "The process cannot access the file because it is being used by another process"
// exception and try again, all other exceptions should not be caught here
throw;
}
Thread.Sleep(100);
}
}
}
Both processes need to create a FileStream where they specify a FileShare mode of Write. You can then also drop the test whether the file exists, and just use the Append FileMode.

Categories