I have a program that stores word documents in a database, and they are repeatedly opened, closed by the user and then saved back to the database.
when opening I place them on a temp folder.
but when closed, I want to save is back to the database and then delete it directly.
I tried this:
...
((DocumentEvents_Event)document).Close += DocumentClose;
...
delegate void MethodDelegate();
private void DocumentClose()
{
new MethodDelegate(deleteLater)();
}
void deleteLater()
{
//document.Close();
Thread.Sleep(1000);
File.Delete(this.tempDocFilePath);
}
but this don't work, and I get an error message telling me the file is already opened.
and when I uncomment "document.Close();" the next two lines are not excuted
any Ideas ?
This code is possibly subject to a race condition. NEVER trust a Sleep-solution. Wait for a specific event or poll and then take action.
Okay I solved it !
here's my code snippent:
private static Thread oThread = new Thread(new ParameterizedThreadStart(delete));
...
((DocumentEvents_Event)document).Close += DocumentClose;
...
private static void DocumentClose()
{
oThread.Start(path);
}
static void delete(object path)
{
try
{
File.Delete((string)path);
}
catch (Exception)
{
Thread.Sleep(500);
delete(path);
}
}
Related
What I'm trying to do is watch a directory for new folders that are added.
Once a folder is added, I copy a standard directory structure from a different folder into this one and then use Open Office XML to do a Search and Replace on the contents of one of the Microsoft Word documents that is included in the source folders.
All is working great... EXCEPT:
1) If I copy more than one folder to the "watched" directory, one of them will get handled, none of the others will get processed. Also, after that, the app gets "stuck" and needs to be restarted to work again.
2) For some reason, it occasionally gets "lazy" on me and just quits working. Watching the console window it shows that it's at the WaitForChanged part of the code, but nothing happens. This seems to happen inconsistently.
Here is the FileSystemWatcher portion of the code (don't want to bore you with the OOXML and folder copying bits):
class soClass
{
private const string strDirectory = #"C:\[DIRECTORY-TO-WATCH\]";
private static FileSystemWatcher fw = new FileSystemWatcher(strDirectory);
private static void WatchIt()
{
// WHILE TRUE -- JUST MAKES IT RUN OVER AND OVER AGAIN...
while (true)
{
Console.WriteLine("Waiting on file/folder changes...");
// HANGS HERE ON THE 'REG
string strName = fw.WaitForChanged(WatcherChangeTypes.Created, -1).Name;
Console.WriteLine("File/Folder Added!");
Console.WriteLine("Starting over...");
Console.WriteLine("--------------------------------------------------------");
}
}
}
As always, any help is greatly appreciated.
UPDATE:
Here is the final code that worked, thanks to #openshac
class soClass
{
private const string strDirectory = #"C:\[DIRECTORY-TO-WATCH]";
private static void Main()
{
WatchIt();
}
public static void WatchIt()
{
FileSystemWatcher fw = new FileSystemWatcher();
fw.Path = strDirectory;
fw.Created += new FileSystemEventHandler(OnCreated);
Console.WriteLine("Waiting on file/folder changes...");
// BEGIN WATCHING
fw.EnableRaisingEvents = true;
// WAIT FOR USER TO QUIT THE PROGRAM
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
private static void OnCreated(object sender, FileSystemEventArgs fileSystemEventArgs)
{
Console.WriteLine("File/Folder Added!");
string strName = fileSystemEventArgs.Name;
// DO STUFF HERE!!
Console.WriteLine("Starting over...");
//rwlock.ExitWriteLock();
Console.WriteLine("--------------------------------------------------------");
}
}
Also found some help here:
https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx
From the documenation:
This method waits indefinitely until the first change occurs and then
returns.
https://msdn.microsoft.com/en-us/library/67220zhk(v=vs.110).aspx
If you want to listen to multiple events try using the OnCreated event:
var fw = new FileSystemWatcher();
fw.EnableRaisingEvents = true;
fw.Created += OnCreated;
}
private static void OnCreated(object sender, FileSystemEventArgs fileSystemEventArgs)
{
var name = fileSystemEventArgs.Name;
}
Actually, it has been an issue for a while where it simply stops working unfortunately. The only methods I've seen to rectify this is to handle it and restart it.
More here: FileSystemWatcher stops catching events
Good luck. Hopefully one day they fix this.
I have something like :
private void Form1_Load(object sender, EventArgs e)
{
Hide();
string ngdx = "*ngdx";
string atdx = "*atdx";
for (;;)
{
try
{
string[] convertngdx = Directory.GetFiles("D:\\folder", ngdx);
string[] convertatdx = Directory.GetFiles("D:\\folder", atdx);
foreach (var convertngd in convertngdx)
{
File.Move(convertngd, Path.ChangeExtension(convertngd, ".ngd"));
}
foreach (var convertatd in convertatdx)
{
File.Move(convertatd, Path.ChangeExtension(convertatd, ".atd"));
}
}
catch
{
}
}
}
I start my app and every time a .ngdx and .atdx file is send to the folder it automatically converts it to .ngd and .atd.
My problem is that it instantly converts them , and I want it to wait for a second before converting them.
I used System.Threading.Thread.Sleep(1000); but it doesn't quite seem to work,I think because when I run my app the System.Threading.Thread.Sleep(1000); is called and then after a second it is never called again.
The idea is every time a new .ngdx or .atdx is send to the folder I want it to wait for a second before converting them.
As an alternative to using an infinite for loop (which will tie up the UI thread) you could use a FileSystemWatcher. (example)
I have a print form which does the printing jobs.
When I close the print form without printing I click Close button
Close button has
private void Close_Click(object sender, EventArgs e)
{
PublicVariables.PrintData = -1;
PublicVariables.PrintStat = false;
ppc.Document = null;
ppc.Dispose();
streamToRead.Close();
this.Hide();
}
But each time I create a text file to print I delete the old.
Delete method :
public static bool DeleteData()
{
bool result=true;
string pattern = "data??.txt";
string appPath = Path.GetDirectoryName(Application.ExecutablePath);
var matches = Directory.GetFiles(appPath, pattern);
foreach (string file in Directory.GetFiles(appPath).Intersect(matches))
{
try
{
File.Delete(file);
result =true;
}
catch (IOException e1)
{
MessageBox.Show(e1.ToString());
return false;
}
}
return result;
}
But if an IOException occurs can't delete any file.
However form load of all threads I have DeleteData() and this deletes without problem the text data.
Is there a way to delete this text file within in the thread where it's created ?
For those who will advise me to make an hidden form which will delete data. I did it I got always an IOexception error. After few IOexception errors all data??.txt files are erased but it happens randomly.
Here below two procedures which create data??.txt
http://www.turcguide.com/stack/procedures.txt
Here is the CreateDataFile(string fName) and GetNewfName(string oldName) procedures link:
http://turcguide.com/stack/createdatafile.txt
The printpreviewcontroler should block this file as when we cancel a printing document on printer it needs time.
After closing and disposing the printpreviewcontroller form, when I try to delete 4-5 attempts later the file is deleted.
My point of view it comes from printpreviewcontroller.
I am completely lost in what is really causing the problem. So rather trying to explain the problem, I might as well as get straight to the code with the problem. Here is the layout of my program:
private void connection_OnMessage(object sender, agsXMPP.protocol.client.Message msg)
{
if (!string.IsNullOrEmpty(msg.Body) && ((msg.XDelay != null && msg.XDelay.Stamp.Date == DateTime.Today) || msg.XDelay == null))
{
agsXMPP.Jid JID = new Jid(msg.From.Bare);
int rowIndex = chatLog.Rows.Add();
chatLog.Rows[rowIndex].Cells["chatNameColumn"].Value = JID.User;
chatLog.Rows[rowIndex].Cells["chatMessageColumn"].Value = msg.Body;
//Begin line of the problem
if (IncomingMessage != null)
IncomingMessage(this, JID.User, msg.Body);
//End of the problem
}
}
The above code snippet is of class A. After starting up the program, this class makes the connection to the server. Right after being connected, this code snippet is rapidly fired about 20 times, once per line of message. (There are already about 20 lines of message in the chat log.) Since only one message makes it through the if condition, the lines commented with the problem is only run once. Those lines fire the code snippet below of class B.
(Around the time class A is firing, I have another class like A that fires the similar event to be handled by class B the same way, which will be handled by class C.)
private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
if (temp != null)
temp(sender, user, message);
}
The above code snippet of class B fires the code snippet below of class C.
private void chatManager_UpdatedMessage(IChatSource source, string user, string message)
{
if (!source.Muted)
{
updateMessage(source, user, message);
}
}
delegate void UpdateMessageCallback(IChatSource source, string user, string message);
private void updateMessage(IChatSource source, string user, string message)
{
if (allDataGridView.InvokeRequired)
{
UpdateMessageCallback d = new UpdateMessageCallback(updateMessage);
Invoke(d, new object[] { source, user, message });
}
else
{
int row = allDataGridView.Rows.Add();
allDataGridView.Rows[row].DefaultCellStyle.ForeColor = source.TextColor;
allDataGridView.Rows[row].Cells["NameColumn"].Value = user;
allDataGridView.Rows[row].Cells["MessageColumn"].Value = message;
if (!MenuItem.Checked)
{
MenuItem.Checked = true;
Show();
}
}
}
Here is what I tried to do to fix the problem, but the code is removed already:
I tried adding lock to certain codes.
I tried to put the certain codes on a separate thread and have them run.
Here is what happened:
When I run the program, the UI thread seems to be blocked. In other words, class C doesn't get painted. Sometimes, the form doesn't even appear.
A few times, it complaint about a strange error "An error occurred invoking the method. The destination thread no longer exists."
If I commented out the problem lines, everything work fine, but here is the strange part. If I create a timer object in class A and have it fired the event the same way, it works fine.
While line stepping in debug mode, I sometimes got it work fine, but majority of the time, it fails.
For a few times, I run into InvalidOperationException with the message, "Control accessed from a thread other than the thread it was created on." even though I did make it thread safe.
In conclusion, I don't know what is causing the UI thread to be blocked. Any pointer or what I might do wrong?
The problem is that you're calling methods crossthreading. This can lead to deadlocks.
You could solve this, adding the messages on a concurrent queue and on a timer (gui thread) checking the queue and adding the messages to controls.
This is not a complete solution, but a method to prevent crossthread method invoking
Like: (PSEUDO) (wrote online on the site)
// dataholder
public class ChatMsg
{
public string User {get;set;}
public string Message {get;set;}
}
// message store
private List<ChatMsg> _messages = new List<ChatMsg>();
// timer
private Timer _timer;
// callback for you chatapi?? (like you wrote)
private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
// lock the store
lock(_messages)
_messages.Add(new ChatMsg { User = user, Message = message });
}
// constructor
public Form1()
{
// create the check timer.
_timer = new Timer();
_timer.Interval = 100;
_timer.Tick += Timer_Tick;
_timer.Start();
}
// timer method
private void Timer_Tick(object sender, EventArgs e)
{
// copy of the queue
ChatMsg[] msgs;
// lock the store and 'move' the messages
lock(_messages)
{
msgs = _messages.ToArray();
_messages.Clear();
}
if(msgs.Length == 0)
return;
// add them to the controls
foreach(var msg in msgs)
{
// add the message to the gui controls... (copied from your question)
int row = allDataGridView.Rows.Add();
allDataGridView.Rows[row].DefaultCellStyle.ForeColor = source.TextColor;
allDataGridView.Rows[row].Cells["NameColumn"].Value = user;
allDataGridView.Rows[row].Cells["MessageColumn"].Value = message;
}
}
Something like that..
I have a lock in my c# web app that prevents users from running the update script once it has started.
I was thinking I would put a notification in my master page to let the user know that the data isn't all there yet.
Currently I do my locking like so.
protected void butRefreshData_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(UpdateDatabase));
t.Start(this);
//sleep for a bit to ensure that javascript has a chance to get rendered
Thread.Sleep(100);
}
public static void UpdateDatabase(object con)
{
if (Monitor.TryEnter(myLock))
{
Updater.RepopulateDatabase();
Monitor.Exit(myLock);
}
else
{
Common.RegisterStartupScript(con, AlreadyLockedJavaScript);
}
}
And I do not want to do
if(Monitor.TryEnter(myLock))
Monitor.Exit(myLock);
else
//show processing labal
As I imagine there is a slight possibility that it might display the notification when it isn't actually running.
Is there an alternative I can use?
Edit:
Hi Everyone, thanks a lot for your suggestions! Unfortunately I couldn't quite get them to work...
However I combined the ideas on 2 answers and came up with my own solution. It seems to be working so far but I have to wait for the process to complete...
Ok this seems to be working, I broke out the Repopule Method into it's own class.
public static class DataPopulation
{
public static bool IsUpdating = false;
private static string myLock = "My Lock";
private static string LockMessage = #"Sorry, the data repopulation process is already running and cannot be stopped. Please try again later. If the graphs are not slowly filling with data please contact your IT support specialist.";
private static string LockJavaScript = #"alert('" + LockMessage + #"');";
public static void Repopulate(object con)
{
if (Monitor.TryEnter(myLock))
{
IsUpdating = true;
MyProjectRepopulate.MyProjectRepopulate.RepopulateDatabase();
IsUpdating = false;
Monitor.Exit(myLock);
}
else
{
Common.RegisterStartupScript(con, LockJavaScript);
}
}
}
In master I do
protected void Page_Load(object sender, EventArgs e)
{
if (DataPopulation.IsUpdating)
lblRefresh.Visible = true;
else
lblRefresh.Visible = false;
}
(Given that you are aware of the race condition for displaying this notification just after processing stopped.... )
You could switch to a CountdownEvent. This works similarly to a ManualResetEvent, but also provides CurrentCount and IsSet properies, which could be used to determine if something is being processed.
How about just setting a volaltile bool property somewhere that indicates an active lock, perhaps via callback method?
Explore Autoresetevents and ManualResetevents. You can have the spawned thread set the event and check the event in the main thread to display the message.
butRefreshData_Click()
{
lock(myLock)
{
if (isbusy) {/*tell user*/}
}
}
UpdateDatabase(object con)
{
lock(myLock)
{
if (isbusy) {/*tell user*/ return;}
else {isbusy = true;}
}
Updater.RepopulateDatabase();
lock(myLock)
{
isBusy = false;
}
}
Note: You should probably wrap UpdateDatabase in a try-finally to avoid isBusy from being stuck true if an exception is thrown.
As I imagine there is a slight
possibility that it might display the
notification when it isn't actually
running.
There will always be the possibility you send the "Working..." message and then immediately the job is finished. What you have should logically work.
public static void UpdateDatabase(object con)
{
if (Monitor.TryEnter(myLock))
{
System.Diagnostics.Debug.WriteLine("Doing the work");
Thread.Sleep(5000);
Monitor.Exit(myLock);
System.Diagnostics.Debug.WriteLine("Done doing the work");
}
else
{
System.Diagnostics.Debug.WriteLine("Entrance was blocked");
}
}