I'm creating a memory game (matching game) and I'm trying to save the time the player gets when he/she has matched all the tiles. My streamwriter does work, and I'm curios why. Here's my code for the save method and the method that find if all tiles are matched:
private void Save(string time)
{
StreamWriter write = new StreamWriter(path, true);
write.WriteLine(time);
write.Close();
}
private void CheckForWinner()
{
foreach (Control control in tableLayoutPanel1.Controls)
{
Label iconLabel = control as Label;
{
if(iconLabel != null)
{
if (iconLabel.ForeColor == iconLabel.BackColor)
{
return;
}
}
}
}
MessageBox.Show("You finished the game, your time was: " + timeLabel.Text);
Save();
//Close(); is outcommented because I want to see if it works.
}
You don't seem to be specifying the parameter time when calling Save.
Add the timeLabel.Text to your function call.
EDIT: A good practice for using StreamWriters is to make use of the using command that is available. Since StreamWriter is Disposable you can wrap the usage of it inside a using and not have to worry about closing it. See the updated Save function.
private void Save(string time)
{
using(StreamWriter write = new StreamWriter(path, true)){
write.WriteLine(time);
}
}
private void CheckForWinner()
{
foreach (Control control in tableLayoutPanel1.Controls)
{
Label iconLabel = control as Label;
{
if(iconLabel != null)
{
if (iconLabel.ForeColor == iconLabel.BackColor)
{
return;
}
}
}
}
MessageBox.Show("You finished the game, your time was: " + timeLabel.Text);
Save(timeLabel.Text);
//Close(); is outcommented because I want to see if it works.
}
Save should looks like:
private void Save(string time)
{
File.WriteAllText(path, time); // Or AppendAllText, depends on what you want.
}
And inside CheckForWinner you have to call not Save() but Save(timeLabel.Text).
Related
Is there an Event I can capture for when a known file has been closed by an external application?
For example, a user is editing a workbook in Excel and I want to read that file as soon as the user finishes working on it and closes the file.
My current solution is to use a combination of FileSystemWatcher and Timer. The FileSystemWatcher will detect when changes have been made to a file, and start a new thread running a Timer to check when the file has closed (via try-catch) However I don't feel as though this is a good solution. If the user forgot to close the file and heads home for the weekend, it feels wasteful for my Timer to be running the whole time. If I increase the interval on my Timer, then my program won't be as responsive. Is there a solution that doesn't involve polling?
EDIT: updated with code example of what I have
private System.Windows.Forms.Timer processTimer;
private string blockedFile;
// Starts here. File changes were detected.
private void OnFileSystemWatcher_Changed(object source, FileSystemEventArgs e)
{
FileSystemWatcher fsw = (FileSystemWatcher)source;
string fullpath = Path.Combine(fsw.Path, fsw.Filter);
StartFileProcessing(fullpath);
}
private void StartFileProcessing(string filePath)
{
if (isFileOpen(new FileInfo(filePath)))
{
blockedFile = filePath;
processTimer = new System.Windows.Forms.Timer();
processTimer.Interval = 1000; // 1 sec
processTimer.Tick += new EventHandler(processTimer_Elapsed);
processTimer.Enabled = true;
processTimer.Start();
}
else
ProcessFile(filePath);
}
private void ProcessFile(string filePath)
{
// Do stuff, read + writes to the file.
}
// GOAL: Without polling, how can I get rid of this step just know right away when the file has been closed?
private void processTimer_Elapsed(object sender, EventArgs e)
{
if (isFileOpen(new FileInfo(blockedFile)) == false)
{
// The file has been freed up
processTimer.Enabled = false;
processTimer.Stop();
processTimer.Dispose();
ProcessFile(blockedFile);
}
}
// Returns true if the file is opened
public bool isFileOpen(FileInfo file)
{
FileStream str = null;
try
{
str = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
return true;
}
finally
{
if (str != null)
str.Close();
}
return false;
}
I have a small application that watches a specific file and whenever it changes, my application should do the actions in the loop, but something is firing the function more than once!! here's my code
private void OnChanged(object source, FileSystemEventArgs e)
{
if (e.FullPath == #"C:\test.txt")
{
string textFilePath = #"C:\test.txt";
try
{
using (var streamReader = File.OpenText(textFilePath))
{
var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
//actions here
}
}
}
catch (Exception)
{
}
}
}
So I'm guessing in the loop when streamreader do File.OpenText somehow is firing the function again?! any ideas?
From MSDN:
The Changed event is raised when changes are made to the size, system attributes, last write time, last access time, ...
So yes, opening (actually: closing) the file will raise the Changed event again.
You can use the NotifyFilter to limit the actions your watcher triggers on.
SOLUTION
So I did one small thing that controlled the issue, I added a counter and always check if it's not the first time, skip and reassign it to 0.
private int fireCounter = 0;
private void OnChanged(object source, FileSystemEventArgs e)
{
fireCounter++;
if (fireCounter == 1)
{
delete();
if (e.FullPath == #"C:\test.txt")
{
Thread.Sleep(2000);
//I added Sleep for two seconds because without it sometimes it wont work
string textFilePath = #"C:\test.txt";
try
{
using (var streamReader = File.OpenText(textFilePath))
{
var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
//Actions Here
}
}
}
catch (Exception)
{
}
}
}
else
{
fireCounter = 0;
}
}
I have a function in my program that exports data. The exporting works great, but I'm having trouble with something: When the user clicks Export, they are presented with a folder browser to choose where on their hard drive they would like to export to. The browser launches initially with all the hard drives/folders collapsed like they should be, but I can't figure out how to make it so the program remembers which location the user chose, so that the next time they want to export, it automatically opens to that location instead of once again opening with everything collapsed. I'm just having trouble with the logic, I suppose. Anyone have any tips?
Also, just for clarification, I'm trying to get it to remember the location only for the duration of the session, not like permanently on the register.
Here's my export function so far, if you think that would be helpful:
private void Export(int formatVersion, bool pureXmlDriver)
{
if (Device != null)
{
Utilities.StripShortNameFromLongNames(Device);
using (var folderBrowser = new FolderBrowserDialog())
{
folderBrowser.Description = Resources.SelectExportFolder;
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
string selectedFolder = folderBrowser.SelectedPath;
try
{
Cursor = Cursors.WaitCursor;
HandleExport(formatVersion, pureXmlDriver, selectedFolder);
}
finally
{
Cursor = Cursors.Default;
}
}
}
}
}
This should do it. You just need a class field to keep the last value in.
public class MyClass
{
private string selectedPath = "";
public void Export(int formatVersion, bool pureXmlDriver)
{
if (Device != null)
{
Utilities.StripShortNameFromLongNames(Device);
using (var folderBrowser = new FolderBrowserDialog())
{
folderBrowser.Description = Resources.SelectExportFolder;
folderBrowser.SelectedPath = selectedPath;
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
selectedFolder = folderBrowser.SelectedPath;
try
{
Cursor = Cursors.WaitCursor;
HandleExport(formatVersion, pureXmlDriver, selectedFolder);
}
finally
{
Cursor = Cursors.Default;
}
}
}
}
}
}
When the code is working so laggy it would be very good so that it is not laggy when it works.
How the code works:
It searches the computer for a file is then when it find it to change the file but if the file is running line will loop until it manages to do its job.
Main class
public Form1(string[] Args)
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000); // One second.Thread.Sleep(1000); // One second.
MessageBox.Show("Testing");
Fille mc = new Fille();
mc.Search();
}
Fille clss
private static ArrayList list2 = new ArrayList();
private static ArrayList listRemove = new ArrayList();
public void Search()
{
try
{
foreach (string file in Directory.EnumerateFiles(#"C:\Users\user\Downloads\MCFILE\trrtrt\", "*.exe", SearchOption.AllDirectories))
{
// Display file path.
if (SHA1Hash.GetSHA1Hash(file) == "1233456") // fake SHA1Hash
{
try
{
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", file);
}
catch (IOException)
{
// log errors
if (list2.Count == 0)
{
list2.Add(file);
Thread thread = new Thread(new ThreadStart(Test2));
thread.Start();
}
else
{
Thread thread = new Thread(new ThreadStart(Test2));
thread.Abort();
list2.Add(file);
thread.Join();
}
}
}
}
}
catch (Exception ex)
{
// log errors
}
}
private void Test2()
{
if (list2.Count == 0)
{
}
else
{
foreach (string _item in list2)
{
try
{
//Run
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", _item);
listRemove.Add(_item);
}
catch (IOException)
{
//error
}
}
foreach (var Remove in listRemove)
{
list2.Remove(Remove);
}
listRemove.Clear();
if (list2.Count == 0)
{
}
else
{
Thread thread = new Thread(new ThreadStart(Test2));
thread.Start();
}
}
}
I made a new thread because I found the problem. But now it's just that it lags.
I suspect the reason it's "lagging" is because you have the system in a very convoluted but rather processor intensive and I/O intensive loop. If a file fails the first test, your code starts a thread that tries it again. And if that fails then you start another thread to try it again, lather, rinse, repeat.
That's going to absolutely kill performance. You're basically doing this:
while (forever)
{
if I can overwrite the file
{
break;
}
}
Except if you have multiple files that you're trying to write, then you're doing that loop for every file. Concurrently. And you're not just using a loop. Instead, you're starting and stopping threads like nobody's business.
Yeah, that's going to slow down your computer.
A more reasonable way to do this would be with a thread to do the first check, and a timer that will limit how often you do the other checks. Communication is with a simple queue, because only one thread will be accessing it at a time.
Here's what I would recommend:
private static Queue<string> filesToCheck = new Queue<string>();
private System.Timers.Timer copyTimer;
public void Search()
{
try
{
foreach (string file in Directory.EnumerateFiles(#"C:\Users\user\Downloads\MCFILE\trrtrt\", "*.exe", SearchOption.AllDirectories))
{
// Display file path.
if (SHA1Hash.GetSHA1Hash(file) == "1233456") // fake SHA1Hash
{
if (!TryToCopy(file)) // See function below
{
filesToCheck.Enqueue(file);
}
}
}
// Checked all the files once.
// If there are any in the queue, start the timer.
if (filesToCheck.Count > 0)
{
copyTimer = new System.Timers.Timer(CopyTimerProc, null, 1000, Timeout.Infinite);
}
}
catch (Exception)
{
// do your error handling
}
}
private void CopyTimerProc(object state)
{
string filename = filesToCheck.Dequeue();
if (TryToCopy(filename))
{
// success. If the queue is empty, kill the timer.
if (filesToCheck.Count == 0)
{
copyTimer.Dispose();
}
}
else
{
// File still locked.
// Put it back on the queue and reset the timer.
filesToCheck.Enqueue(filename);
copyTimer.Change(1000, 0);
}
}
private bool TryToCopy(string filename)
{
try
{
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", filename);
return true;
}
catch (IOException)
{
// log error
return false;
}
}
The timer is a one-shot that is reset after each time it ticks. The reason I did it this way is to prevent another tick coming along while the previous tick is still processing. After all, it takes time to copy a file.
There's no reason to do this with a bunch of threads. The file system can only do one thing at a time, anyway, and it's not like an extra second or two while you wait for a file to become available is going to hurt anything.
Iam Unable to do this from past one week. I want to click on multiple links n multiple web pages using webBrowser in C# Following is the code please help me in this regard.
public void DoDelete()
{
int count = 0;
if (corruptList.Count > 0)
{
foreach (string listItem in corruptList)
{
var th = new Thread(() =>
{
try
{
WebBrowser webBrowser = new WebBrowser();
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBroswer_DocumentCompleted);
webBrowser.Navigate(listItem);
Thread.Sleep(100);
webBrowser.Dispose();
}
catch (Exception ex)
{
throw ex;
}
this.Invoke(new MethodInvoker(delegate
{
dataGridView_CorruptLinks.Rows[count].Cells[2].Value = "Deleted";
}));
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
Thread.Sleep(100);
}
count++;
}
}
void webBroswer_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
WebBrowser webBrowser = sender as WebBrowser;
HtmlElementCollection ec = webBrowser.Document.GetElementsByTagName("a");
foreach (HtmlElement item in ec)
{
if (item.InnerHtml == "Delete this invalid field")
{
item.InvokeMember("Click");
break;
}
}
}
catch (Exception exp)
{
}
}
Navigate is an asynchronous action and you're only giving it 1/10 of a second to complete before you call Dispose on the web browser object. Your navigation and clicks are probably taking longer than that to complete and so there is no web browser to act against... You're also "swallowing" all exceptions in the document complete handler. This is a very bad thing to do. You should at the very least be doing some debug logging there to help yourself diagnose the problem.
But, to keep the similar logic you should create a collection of web browsers at class level. Something like:
private List<WebBrowser> _myWebBrowsers;
Then add to this list in your loop but do not call Dispose. You should only dispose of the browser when you're done with it.
That should get you closer though there are a few other potential issues with your code. You're allocating a borser object and thread for every time through a loop. This could quickly become unwieldy. You should use a thread management mechanism to throttle this process.
Simplified class:
class WebRunner
{
private List<string> _corruptList = new List<string>();
private List<WebBrowser> _browsers = new List<WebBrowser>();
public void Run()
{
_corruptList.Add("http://google.com");
_corruptList.Add("http://yahoo.com");
_corruptList.Add("http://bing.com");
DoDelete();
Console.ReadKey();
}
public void DoDelete()
{
if (_corruptList.Count < 1) return;
int counter = 1;
foreach (string listItem in _corruptList)
{
WebBrowser webBrowser = new WebBrowser();
_browsers.Add(webBrowser);
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBroswer_DocumentCompleted);
webBrowser.Navigated += new WebBrowserNavigatedEventHandler(webBrowser_Navigated);
webBrowser.Navigate(listItem);
if (counter % 10 == 0) Thread.Sleep(3000); // let app catch up every so often
counter++;
}
}
void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
Console.WriteLine("NAVIGATED: " + e.Url);
}
void webBroswer_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Console.WriteLine("COMPLETED!");
try
{
WebBrowser webBrowser = sender as WebBrowser;
HtmlDocument doc = webBrowser.Document;
var button = doc.Body.Document.GetElementById("button");
button.InvokeMember("Click");
_browsers.Remove(webBrowser);
}
catch (Exception exp)
{
Console.WriteLine(exp.StackTrace);
MessageBox.Show(exp.Message);
}
}
}
You can access the WebBrowser document content using the following (you are missing body and need to type document to dynamic).
dynamic doc = browser.Document;
var button = doc.body.document.getElementById("button");
button.Click();
I found the solution very next day. Sorry for the late post by processing threads one by one by putting the statement after thread.sleep()
if (th.ThreadState == ThreadState.Aborted || th.ThreadState == ThreadState.Stopped)