A program is running on multiple machines that share a network drive. It can use
... = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
to lock a file from writing. All the other instance then can only read it and display a warning, that the file is not writable.
How can I find out who (i.e. which machine) locked the file, to display that along the warning?
The only way I have ever seen this achieved is for the program that opens the file to leave behind a marker file, (.lock) or similar. This .lock file can then obviously contain whatever you want (username, machine etc) and can be read separately.
This assumes you have control over the software which is reading it on the other PC.
Here is a posting with C# source code for an example of how to look through the process list and check the files that are locked by each process.
How does one figure out what process locked a file using C#.
The next step would be to use this functionality within a service on each machine so that a process can send a query for a specific file name and then receive a response as to whether a process on that machine has it locked.
The data could include process name, user id, and other information available from the process list.
This approach is more work however what it does is provide a way to access the information without require applications locking the file to do something special.
On the other hand if the files you are interested in are within your control and you can determine the file access, this is probably overkill.
Related
Language used: C#
Theory:
I want to create a file with the flag FileOptions.DeleteOnClose in a temporary folder.
The file is successfully created and I write dato onto it, the next step is to launch the application associated with the file Process.Start(...) and allow the user to inspect the document, finally I close my handle and as soon as other process close the handle to the temporary file, the file is deleted by operating system.
My problem is that other processes cannot open the file, even for reading, despite if I add FileShare.ReadWrite | FileShare.Delete to the sharing mode.
Any suggestions?
The other processes need to specify FileShare.Delete when they open the DeleteOnClose file
From the MSDN CreateFile docs:
"FILE_FLAG_DELETE_ON_CLOSE... Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified."
Check this:
You need to make sure that all processes are opening the file with FileShare.ReadWrite and FileShare.Delete.
Even if the creator opens with share-readwrite, if a second program tries to open with share-read, the second program is basically saying no-one else can write. But the first program already has that power so the second open fails.
Switch to Linux scnr
Ok, seriously now: That is a flaw in the Windows operating system which can't really be worked around. Each program opening the file must agree on other programs having the file open in the same time. That was a problem I got many years back when I still used Windows as well. It doesn't suffice to open a file and say: Let anyone else open this as well. The others must also say open this file even if it's open already.
On Linux on the contrary, the operating system doesn't allow any file locking in the way Windows does at all. Here, if any file is used by more than one program simultaneously, the programs itself must make sure, that concurrent accesses get locked out. Additionally, on Linux, we can just create the file, make sure the other process has been started and opened the file and then just delete the file (while it is open). The filename is then removed from the file system immediatelly, but the file is still maintained by the file system driver until the last link (including open file handles) got removed.
Back to your problem: As all of this doen't work on Windows, you could do two other approaches:
Register the file to be deleted on next boot (in the Win3x days, there was a section in the win.ini for that. Newer Windows version still support that, I just can't recall any longer, how it's done now).
Start the other process, wait for it to open the file, close the file and then try each minute to delete the file until deletion succeeds ...
Regards, Bodo
I'm writing an application that needs to be notified of every doc file that is opened, I've tried using the FileSystemWatcher but it seems that these days NotifyFilter.LastAccess is disabled due to a large overhead.
There is LastWrite which I suppose I could use but it would mean I'd need to try and figure out the original file name from the temporary file that word creates when it opens a document.
I also need to keep watch on 4 directories so ideally I don't want to be polling them.
I'm aware I could write a WordAddin which is one option but that means another deployment to manage, another codebase and another product to support along with the problem that many users always see addins as a source of slowdowns.
Is there a straightforward way to tell windows Vista upward that I want to know about doc or docx that is opened?
One thing I was wondering about is if I could alter the default program associated with .doc to mine, which is running as a service and then passing the details through it to mine to be opened? This seems like a bit of a hack so I was wondering if there was an easy way to hook into these sorts of file open?
UPDATE
From talking it through with various people here the most reliable way(and most resource effective) would seem to be to replace the existing file association for .doc. & .docx to my own program and then use Microsoft.Office.Interop.Word to launch word and then hook into the DocumentOpen event.
That way I get the file name thats being opened along with any future documents that are open in word.
If I recall correctly, the temporary file that is created in the same folder has the file name format of ~$ + filename, for example:
~$very_important_file.doc
It contains the name of the user that opened the file. Note that the file has the hidden attribute set.
This makes it quite easy to figure out which document is actually open and by whom.
Such tasks are usually accomplished using filesystem filter drivers. Procmon works this way. You can create your own filter driver or use the precreated one (eg. our CallbackFilter).
I need to read a text based log file to check for certain contents (the completion of a backup job). Obviously, the file is written to when the job completes.
My question is, how can I (or how SHOULD I write the code to) read the file, taking into account the file may be locked, or locked by my process when it needs to be read, without causing any reliability concerns.
Assuming the writing process has at least specified System.IO.FileShare.Read when opening the file, you should be able to read the text file while it is still being written to.
In addition to the answer by #BrokenGlass:
Only open the file for reading. If you try to open it for Read/Write access, it's more likely (almost certain) to fail - you may not be able to open it, and/or you may stop the other process being able to write to it.
Close the file when you aren't reading it to minimise the chance that you might cause problems for any other processes.
If the writing process denies read access while it is writing to the file, you may have to write some form of "retry loop", which allows your application to wait (keep retrying) until the file becomes readable. Just try to open the file (and catch errors) - if it fails, Sleep() for a bit and then try again. (However, if you're monitoring a log file, you will probbably want to keep checking it for more data anyway)
When a file is being written to, it is locked for all other processes that try to open the file in Write-mode. Read-mode will always be available.
However, if your writing process saves changes while you have already opened the file in your reading process, the changes will not be reflected there until you refresh (Close-Open) the file again.
I have many processes reading a file stored on a network share. Originally I was only able to have one process read the file, all the others would throw exceptions. I implemented the following code to deal with that:
using (StreamReader fileStreamReader = new StreamReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
content = fileStreamReader.ReadToEnd();
}
This let multiple processes read the same file, however it still seems to have issues, because sometimes multiple processes still can't access the file. Yet I can go back later when the file isn't in use and open it just fine. Right now I have some retry behavior with random delays implemented that so far, seem to help. It seems a little quirky to me to do it this way, so what would be a better method?
This is the weird part, the exception I'm getting is not from file IO at all, it's from a library called CommStudio. In short, I dump the file to a string, i modify it slightly, dump it into a memory stream, and ship it off over ymodem on rs232. The exception is telling me the remote system has canceled. The device getting the data reports that there was a transmission error, which usually means that an incomplete/empty file was received.
Normally I would blame the library on this, but it works flawlessly at desk-testing and when there is only one process accessing the file. The only thing that really seems to be consistent is that it is likely to fail when multiple processes are accessing a file.
had a similar problem but not allot of time to find an ideal solution. I created a webservice and stuck the file local to the webservice app.. then created a simple one liner GET API which was called over the office intranet.. thus ensureing only the calling application edited the log file.. messy but functional.
I have had a similar problem in the past. Try changing how you access the file to something like this.
//Use FileInfo to get around OS locking of the file
FileInfo fileInfo = new FileInfo(path);
//I actually wanted unblocked read write access so change your access and share appropriately
using (FileStream fs = fileInfo.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
//I'm using CopyTo but use whatever method matches your need
fileInfo.CopyTo(Path.Combine(destination, fileName), false);
}
Language used: C#
Theory:
I want to create a file with the flag FileOptions.DeleteOnClose in a temporary folder.
The file is successfully created and I write dato onto it, the next step is to launch the application associated with the file Process.Start(...) and allow the user to inspect the document, finally I close my handle and as soon as other process close the handle to the temporary file, the file is deleted by operating system.
My problem is that other processes cannot open the file, even for reading, despite if I add FileShare.ReadWrite | FileShare.Delete to the sharing mode.
Any suggestions?
The other processes need to specify FileShare.Delete when they open the DeleteOnClose file
From the MSDN CreateFile docs:
"FILE_FLAG_DELETE_ON_CLOSE... Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified."
Check this:
You need to make sure that all processes are opening the file with FileShare.ReadWrite and FileShare.Delete.
Even if the creator opens with share-readwrite, if a second program tries to open with share-read, the second program is basically saying no-one else can write. But the first program already has that power so the second open fails.
Switch to Linux scnr
Ok, seriously now: That is a flaw in the Windows operating system which can't really be worked around. Each program opening the file must agree on other programs having the file open in the same time. That was a problem I got many years back when I still used Windows as well. It doesn't suffice to open a file and say: Let anyone else open this as well. The others must also say open this file even if it's open already.
On Linux on the contrary, the operating system doesn't allow any file locking in the way Windows does at all. Here, if any file is used by more than one program simultaneously, the programs itself must make sure, that concurrent accesses get locked out. Additionally, on Linux, we can just create the file, make sure the other process has been started and opened the file and then just delete the file (while it is open). The filename is then removed from the file system immediatelly, but the file is still maintained by the file system driver until the last link (including open file handles) got removed.
Back to your problem: As all of this doen't work on Windows, you could do two other approaches:
Register the file to be deleted on next boot (in the Win3x days, there was a section in the win.ini for that. Newer Windows version still support that, I just can't recall any longer, how it's done now).
Start the other process, wait for it to open the file, close the file and then try each minute to delete the file until deletion succeeds ...
Regards, Bodo