Monitoring file changes and changing data within the file...
-
What would be a good way to monitor a .ini file? What I want to do is when the file has changed, I need to read the file and find specific text and change it and save the file. I've attempted the file system watcher with little luck. The problem is it is firing before the file is unlocked. I have also tried to put a method in there to check if the file was locked which basically opens the file and on exception returns true if locked otherwise false. I have also tried a looping method like this:
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(file, false);
sw.Write(sb.ToString());
sw.Close();// Log EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information); } catch (IOException) { // Keep trying till the file is unlocked WriteFile(file, sb, oldValue); } finally { if (sw != null) { sw.Close(); sw.Dispose(); } } }
It still errored out after it changes the value a couple times in a row. It changes it a couple times but then ends up with a uncaught IOException somehow
-
What would be a good way to monitor a .ini file? What I want to do is when the file has changed, I need to read the file and find specific text and change it and save the file. I've attempted the file system watcher with little luck. The problem is it is firing before the file is unlocked. I have also tried to put a method in there to check if the file was locked which basically opens the file and on exception returns true if locked otherwise false. I have also tried a looping method like this:
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(file, false);
sw.Write(sb.ToString());
sw.Close();// Log EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information); } catch (IOException) { // Keep trying till the file is unlocked WriteFile(file, sb, oldValue); } finally { if (sw != null) { sw.Close(); sw.Dispose(); } } }
It still errored out after it changes the value a couple times in a row. It changes it a couple times but then ends up with a uncaught IOException somehow
You can use this method in a loop until the ini file closed.
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;
}
-
What would be a good way to monitor a .ini file? What I want to do is when the file has changed, I need to read the file and find specific text and change it and save the file. I've attempted the file system watcher with little luck. The problem is it is firing before the file is unlocked. I have also tried to put a method in there to check if the file was locked which basically opens the file and on exception returns true if locked otherwise false. I have also tried a looping method like this:
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(file, false);
sw.Write(sb.ToString());
sw.Close();// Log EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information); } catch (IOException) { // Keep trying till the file is unlocked WriteFile(file, sb, oldValue); } finally { if (sw != null) { sw.Close(); sw.Dispose(); } } }
It still errored out after it changes the value a couple times in a row. It changes it a couple times but then ends up with a uncaught IOException somehow
JD86 wrote:
It changes it a couple times but then ends up with a uncaught IOException somehow
catch (IOException)
{
// Log the exception here!!// Keep trying till the file is unlocked WriteFile(file, sb, oldValue);
}
You should log all exceptions; I'm betting that your WriteFile is causing the new exception, which would throw something "unhandled". So, wrap that in a try-construction too, or move it somewhere else. I'd also recommend using a
using
block, as opposed to a try-finally with explicit dispose - it's a bit more readable.Bastard Programmer from Hell :suss: if you can't read my code, try converting it here[^]
-
You can use this method in a loop until the ini file closed.
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;
}
The OP could try this, but there's a race condition here. What happens if he calls this method, and it returns false, and then he opens the file but an external process has taken a lock on the file?
*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
-
What would be a good way to monitor a .ini file? What I want to do is when the file has changed, I need to read the file and find specific text and change it and save the file. I've attempted the file system watcher with little luck. The problem is it is firing before the file is unlocked. I have also tried to put a method in there to check if the file was locked which basically opens the file and on exception returns true if locked otherwise false. I have also tried a looping method like this:
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(file, false);
sw.Write(sb.ToString());
sw.Close();// Log EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information); } catch (IOException) { // Keep trying till the file is unlocked WriteFile(file, sb, oldValue); } finally { if (sw != null) { sw.Close(); sw.Dispose(); } } }
It still errored out after it changes the value a couple times in a row. It changes it a couple times but then ends up with a uncaught IOException somehow
I would be tempted to rewrite this method like this:
private static readonly object SyncLock = new object();
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
try
{
using (StreamWriter sw = new StreamWriter(file, false))
{
sw.Write(sb.ToString());
}
// Log
EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information);
}
catch (IOException)
{
lock (SyncLock)
{
// Wait for 200 milliseconds
Monitor.Wait(SyncLock, 200);
}
// Keep trying till the file is unlocked
WriteFile(file, sb, oldValue);
}
}The
using
statement removes the need to explicitly close the stream, and thelock/Monitor
elements help to pause the next call to WriteFile for 200 milliseconds. Without this, you have a tight loop in there with execution of WriteFile happening immediately on failure.*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
-
What would be a good way to monitor a .ini file? What I want to do is when the file has changed, I need to read the file and find specific text and change it and save the file. I've attempted the file system watcher with little luck. The problem is it is firing before the file is unlocked. I have also tried to put a method in there to check if the file was locked which basically opens the file and on exception returns true if locked otherwise false. I have also tried a looping method like this:
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(file, false);
sw.Write(sb.ToString());
sw.Close();// Log EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information); } catch (IOException) { // Keep trying till the file is unlocked WriteFile(file, sb, oldValue); } finally { if (sw != null) { sw.Close(); sw.Dispose(); } } }
It still errored out after it changes the value a couple times in a row. It changes it a couple times but then ends up with a uncaught IOException somehow
Use the FileSystemWatcher, when it notifies you of a change put an entry on a queue with a retrieval time of 1 second (or 200ms or whatever is appropriate in the future), and have another thread which pulls things off that queue at the right time and actions them. In this case the action is 'read file, find text, replace, write file'. If it fails, re-queue it.
-
Use the FileSystemWatcher, when it notifies you of a change put an entry on a queue with a retrieval time of 1 second (or 200ms or whatever is appropriate in the future), and have another thread which pulls things off that queue at the right time and actions them. In this case the action is 'read file, find text, replace, write file'. If it fails, re-queue it.
Based on the description, I believe this is what the OP has said he's done (all except for the pause bit).
*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
-
I would be tempted to rewrite this method like this:
private static readonly object SyncLock = new object();
private void WriteFile(string file, StringBuilder sb, string oldValue)
{
try
{
using (StreamWriter sw = new StreamWriter(file, false))
{
sw.Write(sb.ToString());
}
// Log
EventLog.WriteEntry("QBiniMonitor", "Successfully removed LASTUSERNAME. Old Value: " + oldValue, EventLogEntryType.Information);
}
catch (IOException)
{
lock (SyncLock)
{
// Wait for 200 milliseconds
Monitor.Wait(SyncLock, 200);
}
// Keep trying till the file is unlocked
WriteFile(file, sb, oldValue);
}
}The
using
statement removes the need to explicitly close the stream, and thelock/Monitor
elements help to pause the next call to WriteFile for 200 milliseconds. Without this, you have a tight loop in there with execution of WriteFile happening immediately on failure.*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
Whoa there Slick! I see a possible StackOverflow waiting to happen if the file never gets unlocked.
A guide to posting questions on CodeProject[^]
Dave Kreskowiak -
Whoa there Slick! I see a possible StackOverflow waiting to happen if the file never gets unlocked.
A guide to posting questions on CodeProject[^]
Dave KreskowiakIndeed, but I'm only working with the OPs original code - yes, if it were my code, I'd add in terminating conditions. As I don't know what the OPs ultimate use case is here, I'm loathe to suggest this - I've just had to assume that the lock has a finite limit.
*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
-
Indeed, but I'm only working with the OPs original code - yes, if it were my code, I'd add in terminating conditions. As I don't know what the OPs ultimate use case is here, I'm loathe to suggest this - I've just had to assume that the lock has a finite limit.
*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
Pete O'Hanlon wrote:
I've just had to assume that the lock has a finite limit.
True. I don't know any more about his requirements than you. I just wanted to point out that it's not the ultimate solution for anyone else just scrounging for code.
Pete O'Hanlon wrote:
I'm loathe to suggest this
Kinda makes you want to take a long shower, doesn't it? ;)
A guide to posting questions on CodeProject[^]
Dave Kreskowiak -
Based on the description, I believe this is what the OP has said he's done (all except for the pause bit).
*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
Sorry for the delay responses! Just had a baby girl :-) Anyways let me explain. I'm setting up a multi-tenant quickbooks environment. The problem is as we probably all know is it wasn't really designed for multi-tenancy. Now everything works fine with what I have except the username is populated on a program level. So in the ProgramData directory (All Users) there is a qbw.ini file that contains LASTUSERNAME=. Each time a user launches quickbooks it reads this information and prepopulated the username field for the company database login. I don't want people seeing other peoples logins. Most just use Admin which isn't a big deal but some use their company name. So I don't want another company seeing that. So my idea is to monitor that file for updates using a file system watcher and changing that value in the INI file to blank. So basically changing something like this: "LASTUSERNAME=Admin" to this: "LASTUSERNAME=" Quickbooks does not place a permanent lock on this file. So we don't have to worry about quickbooks locking it for a long period of time. I'm trying suggestions as we speak. I'm adding an error count to stop the service if an exception happens so many times within a specific time period. Hopefully this will stop an overflow exception like you were talking about. I can make something to restart the service ever so often. Not the best plan but so far thats what I got!
-
Based on the description, I believe this is what the OP has said he's done (all except for the pause bit).
*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington
"Mind bleach! Send me mind bleach!" - Nagy Vilmos
CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier
Alright so here is what I ended up with. It seems to work... catches the IO exceptions and continues running. So far it is working good enough for me
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Text;
using System.Threading;namespace QBiniMonitor
{
public partial class QBMonitor : ServiceBase
{
// Watches the qbw.ini directory
FileSystemWatcher watcher2012;
FileSystemWatcher watcher2011;// Path to the folder where qbw.ini is stored string QB2012 = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%") + @"\\Intuit\\QuickBooks 2012\\"; string QB2011 = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%") + @"\\Intuit\\QuickBooks 2011\\"; // Version enum Version { QB2011, QB2012 } /// /// Constructor /// public QBMonitor() { InitializeComponent(); } /// /// When service starts we need to start our FileSystemWatcher /// protected override void OnStart(string\[\] args) { StartNewWatcher(Version.QB2011); StartNewWatcher(Version.QB2012); } /// /// Starts the FileSystemWatcher for Quickbooks 2012 & 2011 /// private void StartNewWatcher(Version watcherVersion) { if (watcherVersion == Version.QB2011) { watcher2011 = new FileSystemWatcher(); watcher2011.Path = QB2011; watcher2011.Filter = "qbw.ini"; watcher2011.NotifyFilter = NotifyFilters.LastWrite; watcher2011.Changed += new FileSystemEventHandler(watcher\_Changed); // Start the watcher StartStopWatcher(Version.QB2011, true); } else if (watcherVersion == Version.QB2012) { watcher2012 = new FileSystemWatcher(); watcher2012.Path = QB2012; watcher2012.Filter = "qbw.ini"; watcher2012.NotifyFilter = NotifyFilters.LastWrite; watcher2012.Changed += new FileSystemEventHandler(watcher\_Changed); // Start the watcher StartStopWatcher(Version.QB2012, true); } } ///