There are many explanations out there how to empty a log file.
Like:
File.WriteAllText(activeTab.FileName, string.Empty);
But this example and other examples I found all have the same problem. It do not work if the logfile is currently locked by another process.
In ruby there is a task I can use rake log:clear which do not remove, just empty the log files.
I found also that I can this with Powershell using clc <filename>.
The sources are available here now:
https://github.com/PowerShell/PowerShell/blob/c1faf1e6e10fc1ce45e84ef6f49ae7136c67a111/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearContentCommand.cs
But honestly I do not understand how this code works, also it inherits from other classes.
Is there a C# implementation available that I can use in any common program/class?
Turns out that the file can be cleared in my case using this snippet:
var stream = new FileStream(FileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
stream.Close();
Related
I'm trying to find an alternative to using the Restart Manager for checking if a file is locked. I found this accepted answer to the same question. However, the accepted answer contains the following comment that I do not understand: "this solution will not work if the file doesn't have a Write or Read lock on it, i.e. it has been opened (for reading or writing) with FileShare.Read or FileShare.Write access."
I tested this using the following code (ommitted using blocks and Close() for brevity):
static void Main(string[] args)
{
const string fileName = "test.txt";
// This should open the file as described, shouldn't it?
var fi1 = new FileInfo(fileName);
// Test with FileShare.Read / FileShare.Write / FileShare.ReadWrite gives the same result
var fs1 = fi1.Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
var fi2 = new FileInfo(fileName);
Console.WriteLine($"Is file locked? {IsFileLocked(fi2)}");
// Displays always: "Is file locked? True"
Console.ReadLine();
}
This displays always "Is file locked? True" whereas it should display "False" according to the comment.
I tested also the code of this answer which has a similar comment with no luck. Tested also with two seperate processes - as expected no difference.
Looking at the docs, my test results seem resonably - but I'm puzzled by the above mentioned comments.
How else would I open a file e.g. for reading without creating a lock?
The part of the answer that you quoted is incorrect. The mechanism that prevents you from opening an already open file is the share mode, not the desired access type.
When you attempt to open a file that is already in use, the share mode requested is compared against the share mode that the file was opened with. If they don't match up, your call fails.
EDIT: Just to cover all of my bases, the above only holds true on Windows. It is possible to open a file without any sort of mutual exclusion on POSIX-based systems. However, .NET was exclusive to Windows at the time of the quoted answer.
I am trying to stop all other processes from reading and writing to a file so I need to synchronize between process,how can I do it?
It will be helpful if someone will give me a pattern for it, and show me where should I enter the read and write section in the code.
Alright. Easy thing thought if that's your aim. You can use a filestream to open up your file. Like..
using (var stream = new FileStream(
#"C:\files\yourFile.txt",
FileMode.Open,
FileAccess.ReadWrite,
// that's the important parameter to set
// it locks the file from other processes
// as long as the stream persists
FileShare.None)) {
// give it some logic ...
}
Messed up. Didn't note the questions intention, my bad. If you try to close a process from reading/writing to a file. Try to find out which and kill it.
I have a few files in \AppData\Roaming that my app is writing to. I create the files when the application starts like this:
private void Form1_Load(object sender, EventArgs e)
{
DirectoryInfo _File = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"MyApp\myfile.txt"));
}
Later, when I write to the file with a button click, I get an error saying that the file is already in use and cannot be accessed. How would I fix this?
The code to write to the file is correct because when I remove the code above and make the files myself, the application writes to them without any issues. Therefore, I dont think the problem is with the code I use to write to the files. But, here it is for reference:
var myfile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"MyApp\myfile.txt"));
StreamWriter sw = new StreamWriter(myfile);
sw.WriteLine(textBox1.text);
sw.Close();
Thanks in advance for any help!
There are a few concepts at play here and I am not sure that we have enough information to definitively address the root problem, but I will give you a few pointers.
You need to be aware of the FileMode, FileAccess and FileShare enumerations.
The first, FileMode, specifies what you intend to do with regard to the file's existence. There are various options, documented in the link above. mI don't think that you have a problem here, but it bears mentioning.
The second, FileAccess, concerns your intended interaction with the file (read, write, or both). If you ask for access to read, then anyone else who opens the file or had it open already (including that web browser control) must have allowed sharing with other readers.
The final one, FileShare, defines who you are willing to share access to the file with. You can specify that others can read it, others can write to it, both, or neither.
The code that you are using is accessing the file using the very simplest defaults, which may be incompatible with the WebBrowser's access mode. Here's what I'd suggest instead:
var myfile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"MyApp\myfile.txt"));
using (var fs = new FileStream(myfile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) {
using (var sw = new StreamWriter(fs)) {
// You probably want to move to the end of the file before writing...
fs.Seek(0, SeekOrigin.End);
sw.WriteLine(textBox1.text);
sw.Close();
}
}
This very clearly expresses your intent, as well as the fact that you are willing to share with others who might read or write (we know the webbrowser will not write to the file, but for some reason maybe it is trying to open it with write intent anyway).
For file operations (as well as anytime your are accessing unmanaged resources) your best bet is to only grab a handle to the file long enough to perform the operation you want and then release it.
In your case, you are opening the resource, then trying to open it again later. Change this. Don't create the files until you are actually going to do something with it. Also, look into the USING clause. You want to release it as soon as you are done reading or writing from it.
Try to Access the FileInfo object to create/append/write files as follows, the file is already been taken by FileInfo class,
use as following,
FileInfo fi1 = new FileInfo(path);
//Create a file to write to.
using (StreamWriter sw = fi1.CreateText())
{
sw.WriteLine("Hello");
sw.WriteLine("And");
sw.WriteLine("Welcome");
}
//Open the file to read from.
using (StreamReader sr = fi1.OpenText())
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
Console.WriteLine(s);
}
}
Try using using.
using (StreamWriter sw = new StreamWriter(myfile))
{
sw.Write(textBox1.text);
}
I have a log file that is open and in use by another program, and I read it's contents with the following:
Dim strContents As String
Dim x As New FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
Dim objReader As StreamReader
objReader = New StreamReader(x)
strContents = objReader.ReadToEnd()
objReader.Close()
This works for reading the text from the file while it is still in use by the other program, however, immediately after this I need to truncate the text file (without deleting it) so that it is blank again. But the file will still be in use by the other program.
I tried
Dim sWrite As StreamWriter
sWrite = New System.IO.StreamWriter(FullPath, False)
sWrite.Write("")
sWrite.Close()
But I get the "in use by another application" exception. I've tried looking in StackOverflow and googling but I can't seem to find an answer, and I can't find a way to do this with filestreams either, or I would try to use
Dim fs As New FileStream(FullPath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)
Thanks in advance.
There are a fair few other posts on this topic.
Detecting whether a file is locked by another process (or indeed the same process)
How to check for file lock?
Can I simply 'read' a file that is in use?
The solution appears to be:
Try
'Code to read file if its not locked by another app
Catch as System.IO.IOException
Also just an FYI that your log file is unmanaged resource so deterministic finalization will help with this resource contention issue. Use the Using statement for deterministic finalization, eg:
Using fs = New FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)
fs.SetLength(0)
file.Save(fs)
fs.Flush()
End Using
Thats because the other program has a lock on the file the operating system won't let you do what you want to do. Unless you change the other program to no lock on write.
Unfortunately, the solution to the problem is not as simple adding or modifying a few lines code, regardless of the language being used. You are describing a classic example of resource contention.
This type of problem is best solved using an access manager, i.e. another process that arbitrates writes such that your program doesn't overwrite what the other program did and vice versa.
I'm not sure if you can do that until the other process closes the stream. However, you can kill the process that is writing to the file.
Here's an example to do that.
Dim pProcess() As Process = System.Diagnostics.Process.GetProcessesByName("notepad")
For Each p As Process In pProcess
p.Kill()
Next
Check this out: http://vbnetsample.blogspot.com/2007/08/start-and-kill-process.html
I have a logfile that is written by a 3rd party application and I'd like my application to "read" that log file in real/near-time, parse the new log entries and act upon certain events.
My thought was that I could achieve this with a combination of FileSystemWatcher (to signal file changes) and MemoryMappedFile (to continue reading from a certain offset).
However, since this is the first time I'm using MemoryMappedFiles I do run into some issues which probably arise from not understanding the concept correctly (e.g. I'm unable to open the existing File as it's in use by the other process).
I was wondering if someone has an example of how to use MemoryMappedFiles to read a file that is locked by another process?
Thanks,
Tom
EDIT:
From the comments, it looks like Memory Mapped Files won't help me accessing files that have an exclusive lock. However, "tail" tools like, e.g. Baretail (http://www.baremetalsoft.com/baretail/index.php) are able to do just that. It has no problem reading the file that has an exclusive lock from another application in 1s intervals). So, there has to be some way to do this?
EDITEDIT:
To answer my own question, the trick in opening a locked file is, creating the FileStream with the following access flags:
fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);
To answer my own question, the trick in reading a locked file is creating the FileStream with the following access flags:
FileStream fileStream = new System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite);
Now it's just a matter of either doing interval based polling or looking for FileSystemWatcher change events to detect file changes
I'm not sure if MemoryMappedFiles are going to help you. Take a look at FileStream:
var stream = new FileStream(path, FileMode.Open, FileShare.Read);
stream.Seek(offset, SeekOrigin.Begin);
Though if the 3rd-party application has the file locked exclusively, there's not much that you can do about it...
[Begin 2nd EDIT]
One more idea...
If the third party app happens to use a logging framework like NLog, log4net, or System.Diagnostics, you could still write your own Target/Appender/TraceListener and route the messages somewhere that you could look at them (such as a file that is not opened exclusively, to another process, etc).
If your third party app is using a logging framework, we probably would have heard about it by now ;-)
[End 2nd EDIT]
[Begin EDIT]
I think I misread the question. It sounded at first like you were using a third party library that had logging implemented and you wanted to do this parsing from within the program that was generating the logging. Having reread your question, it sounds like you want to "listen" to the log file from outside of the application. If that is the case, my answer probably won't help you. Sorry.
[End EDIT]
I don't have anything to offer about MemoryMappedFiles, but I wonder if you could achieve what you are after by writing a custom listener/target/appender for the 3rd party logging system?
For example, if you are using NLog, you could write a custom Target and direct all of your logging messages there (while also directing them to the "real" Target(s)). This way you get crack at each log message as it is logged (so it is actually real time, not near real time). You could do the same thing with log4net and System.Diagnostics.
Note that NLog even has a "MethodCall" target. To use that one you only have to write a static method with the correct signature. I don't know if log4net has a similar concept to this.
This seems like it would be easier to get working reliably than trying to read and parse the log file as it is being written by the third party software.
If the file is "in use", there isn't anything that can be done about that. It truly is "in use". MemoryMappedFiles are for either reading large amounts of data off the drive or sharing data with other programs. It will not help getting around the "in use" limitation.
Memorymapped files are under the same restrictions as the FileStream you initialize it with, be sure that you initialize your Memory-Mapped-File like this
var readerStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var mmf = MemoryMappedFile.CreateFromFile(readerStream, null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, false);
If some other process have completely locked it even from writing you're in bad luck, not sure if there's a way around that. Perhaps use some timer do detect when the process has stopped writing to it.
I've done something similar just for monitoring log files on a console (as opposed to processing), but the principles are the same. Like you, I use a FileSystemWatcher, and the important logic is in my OnChanged event handler:
case WatcherChangeTypes.Changed:
{
System.IO.FileInfo fi = new FileInfo(e.FullPath);
long prevLength;
if (lastPositions.TryGetValue(e.FullPath, out prevLength))
{
using (System.IO.FileStream fs = new FileStream(
e.FullPath, FileMode.Open, FileAccess.Read))
{
fs.Seek(prevLength, SeekOrigin.Begin);
DumpNewData(fs, (int)(fi.Length - prevLength));
lastPositions[e.FullPath] = fs.Position;
}
}
else
lastPositions.Add(e.FullPath, fi.Length);
break;
}
where lastPositions is
Dictionary<string, Int64> lastPositions = new Dictionary<string, long>();
and DumpNewData is simply
private static void DumpNewData(FileStream fs, int bytesToRead)
{
byte[] bytesRead = new byte[bytesToRead];
fs.Read(bytesRead, 0, bytesToRead);
string s = System.Text.ASCIIEncoding.ASCII.GetString(bytesRead);
Console.Write(s);
}