Relevant code:
private static Thread m_thread = null;
private static Boolean m_stop = false;
public static Boolean Start(SearcherParams pars)
{
Boolean success = false;
if (m_thread == null)
{
// Perform a reset of all variables,
// to ensure that the state of the searcher is the same on every new start:
ResetVariables();
// Remember the parameters:
m_pars = pars;
// Start searching for FileSystemInfos that match the parameters:
m_thread = new Thread(new ThreadStart(SearchThread));
m_thread.Start();
success = true;
}
return success;
}
private static void SearchThread()
{
Boolean success = true;
String errorMsg = "";
// Search for FileSystemInfos that match the parameters:
if ((m_pars.SearchDir.Length >= 3) && (Directory.Exists(m_pars.SearchDir)))
{
if (m_pars.FileNames.Count > 0)
{
// Convert the string to search for into bytes if necessary:
if (m_pars.ContainingChecked)
{
if (m_pars.ContainingText != "")
{
try
{
m_containingBytes =
m_pars.Encoding.GetBytes(m_pars.ContainingText);
}
catch (Exception)
{
success = false;
errorMsg = "The string\r\n" + m_pars.ContainingText +
"\r\ncannot be converted into bytes.";
}
}
else
{
success = false;
errorMsg = "The string to search for must not be empty.";
}
}
if (success)
{
// Get the directory info for the search directory:
DirectoryInfo dirInfo = null;
try
{
dirInfo = new DirectoryInfo(m_pars.SearchDir);
}
catch (Exception ex)
{
success = false;
errorMsg = ex.Message;
}
if (success)
{
// Search the directory (maybe recursively),
// and raise events if something was found:
SearchDirectory(dirInfo);
}
}
}
else
{
success = false;
errorMsg = "Please enter one or more filenames to search for.";
}
}
else
{
success = false;
errorMsg = "The directory\r\n" + m_pars.SearchDir + "\r\ndoes not exist.";
}
// Remember the thread has ended:
m_thread = null;
// Raise an event:
if (ThreadEnded != null)
{
ThreadEnded(new ThreadEndedEventArgs(success, errorMsg));
}
}
private static void SearchDirectory(DirectoryInfo dirInfo)
{
if (!m_stop)
{
try
{
foreach (String fileName in m_pars.FileNames)
{
FileSystemInfo[] infos = dirInfo.GetFileSystemInfos(fileName);
foreach (FileSystemInfo info in infos)
{
if (m_stop)
{
break;
}
if (MatchesRestrictions(info))
{
// We have found a matching FileSystemInfo,
// so let's raise an event:
if (FoundInfo != null)
{
FoundInfo(new FoundInfoEventArgs(info));
}
}
}
}
if (m_pars.IncludeSubDirsChecked)
{
DirectoryInfo[] subDirInfos = dirInfo.GetDirectories();
foreach (DirectoryInfo subDirInfo in subDirInfos)
{
if (m_stop)
{
break;
}
// Recursion:
SearchDirectory(subDirInfo);
}
}
}
catch (Exception)
{
}
}
}
The stop is working fine I wanted to add also a pause button to pause and resume the thread.
I added a button click event but how do I make the pause/resume actions and where?
This is a link for the complete code : https://pastebin.com/fYYnHBB6
You can use the ManualResetEvent object.
Here is the simplified version.
In your class, create the object:
public static ManualResetEvent _mrsevent = new ManualResetEvent(false);
In your thread function, as part of the loops that search for files/directories:
private static void SearchThread()
{
foreach (String fileName in m_pars.FileNames)
{
_mrsevent.WaitOne();
}
}
From outside the thread you can call:
a) _mrsevent.Set(); // resume the thread.
b) _mrsevent.Reset(); // pause
private void Form1_Load(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(serialcek));
CheckForIllegalCrossThreadCalls = false;
t.Start();
}
public void serialcek()
{
ManagementObjectSearcher theSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
try
{
foreach (ManagementObject currentObject in theSearcher.Get())
{
ManagementObject theSerialNumberObjectQuery = new ManagementObject("Win32_PhysicalMedia.Tag='" + currentObject["DeviceID"] + "'");
try
{
serial = theSerialNumberObjectQuery["SerialNumber"].ToString();
textBox1.Text = serial;
}
catch (Exception)
{
// MessageBox.Show("Bişiler oldu bende anlamadım");
}
}
Thread.Sleep(sure);
serialcek();
}
catch (Exception ex)
{
MessageBox.Show("Patladım ben." + ex.Message.ToString());
}
}
use thread. but i can't stop it. thread always run and application used memory high.
give an error a few mins later. maybe i can't use for function true.
It's because you call the thread's method recursively without any exit condition. That will eventually lead to a StackOverflowException.
Instead of this approach, you should place a while loop inside you thread's method. This is of course implemented roughly, just to give you the idea:
bool exitThread;
private void Form1_Load(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(serialcek));
CheckForIllegalCrossThreadCalls = false;
exitThread = false;
t.Start();
}
public void serialcek()
{
ManagementObjectSearcher theSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
while (! exitThread)
{
try
{
foreach (ManagementObject currentObject in theSearcher.Get())
{
ManagementObject theSerialNumberObjectQuery = new ManagementObject("Win32_PhysicalMedia.Tag='" + currentObject["DeviceID"] + "'");
try
{
serial = theSerialNumberObjectQuery["SerialNumber"].ToString();
textBox1.Text = serial;
}
catch (Exception)
{
// MessageBox.Show("Bişiler oldu bende anlamadım");
}
}
}
catch (Exception ex)
{
MessageBox.Show("Patladım ben." + ex.Message.ToString());
}
finally
{
// Moved Sleep into 'finally' block to make it be called even if an exception occurs
Thread.Sleep(sure);
}
}
And then you can exit this thread anytime you like by simply assigning true to exitThread:
private void Button1_Click(object sender, EventArgs e)
{
exitThread = true;
}
I want to change wpf controls status after click button start.
The picture is what I want.
Following is my code
private bool _bWorking = false;
public delegate void UpdateStatusDelegate();
private void SetStatus(bool bEnable)
{
if (bEnable)
{
tbName.IsReadOnly = false;
barStatus.Visibility = Visibility.Hidden;
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
btnClose.IsEnabled = true;
}
else
{
tbName.IsReadOnly = true;
barStatus.Visibility = Visibility.Visible;
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
btnClose.IsEnabled = false;
}
}
internal void UpdateStatus()
{
SetStatus(true);
_bWorking = false;
}
private void ThreadFunc()
{
//for (; ; )
//{
// // do something here
// if (_bWorking == false)
// break;
//}
Thread.Sleep(500);
this.Dispatcher.Invoke(new UpdateStatusDelegate(UpdateStatus));
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
_bWorking = true;
SetStatus(false);
this.UpdateLayout();//this.InvalidateVisual();
try
{
Thread t = new Thread(new ThreadStart(() =>
{
ThreadFunc();
}));
t.IsBackground = true;
t.Name = "test status";
t.Start();
while (t.IsAlive)
{
// wait thread exit
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
_bWorking = false;
SetStatus(true);
}
But actually after I click button start, the UI seems frozen, and the thread exited, the UI become normal.
my VS is VS2010.
Force a WPF control to refresh?
this post is not work for me.
edit summary:
add delegate void UpdateStatusDelegate() and function UpdateStatus() to my code
// wait thread exit
The whole point of a background thread is to not wait for it by blocking your UI thread. Don't.
Instead, have the thread notify your UI when it's done.
Try to use BackgroundWorker component it might help you. For more information check the MSDN article
workable code
public delegate void UpdateStatusDelegate();
private bool _bWorking = false;
private void SetStatus(bool bEnable)
{
if (bEnable)
{
tbName.IsReadOnly = false;
barStatus.Visibility = Visibility.Hidden;
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
btnClose.IsEnabled = true;
}
else
{
tbName.IsReadOnly = true;
barStatus.Visibility = Visibility.Visible;
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
btnClose.IsEnabled = false;
}
}
internal void UpdateStatus()
{
SetStatus(true);
}
private void ThreadFunc()
{
try
{
// use Stopwatch to simulate jobs
var watch = Stopwatch.StartNew();
for (; ; )
{
var elapsedMs = watch.ElapsedMilliseconds;
if (elapsedMs > 10000 // 10 seconds
|| _bWorking == false)
{
break;
}
}
watch.Stop();
this.Dispatcher.Invoke(new UpdateStatusDelegate(UpdateStatus));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
_bWorking = false;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
try
{
_bWorking = true;
SetStatus(false);
Thread t = new Thread(new ThreadStart(() =>
{
ThreadFunc();
}));
t.IsBackground = true;
t.Name = "test status";
t.Start();
//while (t.IsAlive)
{
// wait thread exit
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
_bWorking = false;
SetStatus(true);
}
On my windows phone 7 Mango app I use the Microsoft.Live and Microsoft.Live.Controls references to download and upload on Skydrive. Logging in and uploading files works fine but whenever I call the "client.GetAsync("/me/skydrive/files");" on the LiveConnectClient the Callback result is empty just containing the error: "An error occurred while retrieving the resource. Try again later."
I try to retrieve the list of files with this method.
This error suddenly occured without any change on the source code of the app (I think..) which worked perfectly fine for quite a while until recently. At least I didn't change the code section for up- or downloading.
I tried out the "two-step verification" of Skydrive, still the same: can log in but not download.
Also updating the Live refercences to 5.5 didn't change anything.
Here is the code I use. The error occurs in the "getDir_Callback" in the "e" variable.
Scopes (wl.skydrive wl.skydrive_update) and clientId are specified in the SignInButton which triggers "btnSignin_SessionChanged".
private void btnSignin_SessionChanged(object sender, LiveConnectSessionChangedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
processing = true;
client = new LiveConnectClient(e.Session);
client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(UploadCompleted);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompleted);
client.DownloadProgressChanged += new EventHandler<LiveDownloadProgressChangedEventArgs>(client_DownloadProgressChanged);
infoTextBlock.Text = "Signed in. Retrieving file IDs...";
client.GetAsync("me");
}
else
{
connected = false;
infoTextBlock.Text = "Not signed into Skydrive.";
client = null;
}
}
private void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
if (e.Error == null)
{
client.GetCompleted -= new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.GetCompleted += getDir_Callback;
client.GetAsync("/me/skydrive/files");
client_id = (string)e.Result["id"];
if (e.Result.ContainsKey("first_name") && e.Result.ContainsKey("last_name"))
{
if (e.Result["first_name"] != null && e.Result["last_name"] != null)
{
infoTextBlock.Text = "Hello, " +
e.Result["first_name"].ToString() + " " +
e.Result["last_name"].ToString() + "!";
}
}
else
{
infoTextBlock.Text = "Hello, signed-in user!";
}
}
else
{
infoTextBlock.Text = "Error calling API: " +
e.Error.ToString();
}
processing = false;
}
public void getDir_Callback(object s, LiveOperationCompletedEventArgs e)
{
client.GetCompleted -= getDir_Callback;
//filling Dictionary with IDs of every file on SkyDrive
if (e.Result != null)
ParseDir(e);
if (!String.IsNullOrEmpty(e.Error.Message))
infoTextBlock.Text = e.Error.Message;
else
infoTextBlock.Text = "File-IDs loaded";
}
Yo shall use client.GetAsync("me/SkyDrive/files", null) in place of client.GetAsync("me");
This is my code and it works fine.
private void btnSignIn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
try
{
if (e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
{
client = new LiveConnectClient(e.Session);
client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_GetCompleted);
client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_UploadCompleted);
client.GetAsync("me/SkyDrive/files", null);
client.DownloadAsync("me/SkyDrive", null);
canUpload = true;
ls = e.Session;
}
else
{
if (client != null)
{
MessageBox.Show("Signed out successfully!");
client.GetCompleted -= client_GetCompleted;
client.UploadCompleted -= client_UploadCompleted;
canUpload = false;
}
else
{
canUpload = false;
}
client = null;
canUpload = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void client_GetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
try
{
List<System.Object> listItems = new List<object>();
if (e.Result != null)
{
listItems = e.Result["data"] as List<object>;
for (int x = 0; x < listItems.Count(); x++)
{
Dictionary<string, object> file1 = listItems[x] as Dictionary<string, object>;
string fileName = file1["name"].ToString();
if (fileName == lstmeasu[0].Title)
{
folderID = file1["id"].ToString();
folderAlredyPresent = true;
}
}
}
}
catch (Exception)
{
MessageBox.Show("An error occured in getting skydrive folders, please try again later.");
}
}
It miraculously works again without me changing any code. It seems to be resolved on the other end. I have no clue what was wrong.
I am getting this error in my event logs for a service I put into production:
An unhandled win32 exception occurred
in RivWorks.FeedHandler.exe [5496].
Just-In-Time debugging this exception
failed with the following error:
Debugger could not be started because
no user is logged on.
I have it installed and running under a Win NT global account. I have no idea why it is trying to drop into debugging mode. It was built under the Release model. Running on the 4.0 Framework.
When I run on my dev machine, via an EXE entry point instead of the WinSvc entry point, everything runs just fine - BUT - I am already in "debug" mode.
Any ideas of what to look for?
2010-10-21 - NOTE - Changed the code base.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
using sysIO = System.IO;
using RivWorks.FeedHandler;
using System.Collections;
namespace RivWorks.FeedHandler.Service
{
public partial class FeedListener : ServiceBase
{
#region Declarations
private List<string> _keys = new List<string>();
private System.Threading.Timer _clock = null;
private FileSystemWatcher _watcher;
private BackgroundWorker _worker;
private Queue<string> _queue = new Queue<string>();
private bool _isDequeueing = false;
#endregion
#region Constructor
public FeedListener()
{
InitializeComponent();
}
#endregion
#region Start/Stop
protected override void OnStart(string[] args)
{
try
{
WriteToEventLog("Enter Start", EventLogEntryType.Information);
_keys.AddRange(new string[] { "csv", "xml", "zip", "rivx" });
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorkerProgressChanged);
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerRunWorkerCompleted);
_watcher = new FileSystemWatcher(AppSettings.Default.FTPRootPath, "*.*");
_watcher.IncludeSubdirectories = true;
_watcher.NotifyFilter = sysIO.NotifyFilters.DirectoryName | sysIO.NotifyFilters.FileName | sysIO.NotifyFilters.LastAccess | sysIO.NotifyFilters.CreationTime | sysIO.NotifyFilters.LastWrite;
_watcher.Created += new sysIO.FileSystemEventHandler(fileCreatedOrChanged);
_watcher.Changed += new sysIO.FileSystemEventHandler(fileCreatedOrChanged);
_watcher.EnableRaisingEvents = true;
// check every 15 minutes...
_clock = new System.Threading.Timer(Tick, null, 0, 900000);
WriteToEventLog("Exit Start", EventLogEntryType.Information);
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
this.Stop();
}
}
protected override void OnStop()
{
try
{
_watcher.Dispose();
_watcher = null;
_clock.Dispose();
_clock = null;
_worker.Dispose();
_worker = null;
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
#region Event Handlers
void fileCreatedOrChanged(object sender, sysIO.FileSystemEventArgs e)
{
try
{
WriteToEventLog("Enter fileCreatedOrChanged", EventLogEntryType.Information);
if (!_queue.Contains(e.FullPath))
_queue.Enqueue(e.FullPath);
if (!_isDequeueing)
DeQueue();
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
#region Do work on another Thread
void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
try
{
WriteToEventLog("Enter BackgroundWorkerDoWork", EventLogEntryType.Information);
BackgroundWorker bw = sender as BackgroundWorker;
WriteToEventLog("Create Handler", EventLogEntryType.Information);
RivWorks.FeedHandler.Library.Handler handler = new RivWorks.FeedHandler.Library.Handler(Convert.ToBoolean(AppSettings.Default.InProduction), AppSettings.Default.ArchivePath);
WriteToEventLog("Setup Handler", EventLogEntryType.Information);
handler.Keys = _keys;
handler.RootDirectory = AppSettings.Default.RootDirectory;
handler.FtpPath = AppSettings.Default.FTPRootPath;
handler.WorkPath = AppSettings.Default.WorkPath;
handler.ArchivePath = AppSettings.Default.ArchivePath;
handler.EmailHost = AppSettings.Default.EmailHost;
handler.EmailPassword = AppSettings.Default.EmailPassword;
handler.EmailUser = AppSettings.Default.EmailUser;
handler.ErrorNotificationRecipients = AppSettings.Default.ErrorNotificationRecipients;
handler.InProduction = Convert.ToBoolean(AppSettings.Default.InProduction);
Library.DTO.FileHandler fileHandler = new Library.DTO.FileHandler(handler.FtpPath, handler.WorkPath, handler.ArchivePath, (string)e.Argument);
WriteToEventLog("Call Handler.Execute", EventLogEntryType.Information);
handler.Execute(bw, e, fileHandler);
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
finally
{
WriteToEventLog("Exit BackgroundWorkerDoWork", EventLogEntryType.Information);
}
}
void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
try
{
if (e.ProgressPercentage >= 100)
{
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Cancelled)
{
WriteToEventLog("Cancelled.", EventLogEntryType.Warning);
if (e.Error != null)
{
WriteToEventLog(e.Error, EventLogEntryType.Error);
}
}
else if (e.Error != null)
{
WriteToEventLog(e.Error, EventLogEntryType.Error);
}
else
{
WriteToEventLog("Successfully completed.", EventLogEntryType.Information);
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
#region Private Methods
private void Tick(object data)
{
try
{
if (!_isDequeueing)
{
WriteToEventLog("Enter Tick. FTP Root = " + AppSettings.Default.FTPRootPath, EventLogEntryType.Information);
foreach (string key in _keys)
{
List<string> files = Directory.GetFiles(Path.Combine(AppSettings.Default.FTPRootPath), "*." + key, SearchOption.AllDirectories).ToList();
foreach (string fileName in files)
{
if (File.Exists(fileName))
{
// Toss this file name into the Queue...
WriteToEventLog("Call _queue.Enqueue(" + fileName + ")", EventLogEntryType.Information);
if (!_queue.Contains(fileName))
_queue.Enqueue(fileName);
}
}
}
// Now, start handling the list of files...
DeQueue();
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
finally
{
WriteToEventLog("Exit Tick", EventLogEntryType.Information);
}
}
private void DeQueue()
{
try
{
_isDequeueing = true;
WriteToEventLog("Enter DeQueue", EventLogEntryType.Information);
while (_queue.Count > 0)
{
string queuedFile = _queue.Dequeue();
WriteToEventLog("DeQueued " + queuedFile, EventLogEntryType.Information);
bool isValid = false;
foreach (string key in _keys)
{
if (Path.GetExtension(queuedFile).Replace(".", "").Equals(key, StringComparison.CurrentCultureIgnoreCase))
isValid = true;
}
if (isValid)
{
// Now, spin up a new thread and do the work on the file, based on file type...
WriteToEventLog("Call RunWorkerAsync", EventLogEntryType.Information);
string UserName = Path.GetDirectoryName(queuedFile).Replace(AppSettings.Default.FTPRootPath, "").Replace("\\", "");
int i = 0;
DateTime sTime = DateTime.Now;
DateTime eTime = DateTime.Now;
_worker.RunWorkerAsync(queuedFile); // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) //
while(_worker.IsBusy)
{
System.Threading.Thread.Sleep(5000);
i++;
}
eTime = DateTime.Now;
TimeSpan ts = new TimeSpan(eTime.Ticks - sTime.Ticks);
string msg = String.Format("Import for {0} started at {1} and ended at {2}. It took {3} cycles and the elapsed time was {4}:{5}:{6}.", UserName, sTime, eTime, i, ts.Hours, ts.Minutes, ts.Seconds);
WriteToEventLog(msg, EventLogEntryType.Information);
}
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
finally
{
_isDequeueing = false;
WriteToEventLog("Exit DeQueue", EventLogEntryType.Information);
}
}
private void WriteToEventLog(Exception ex, EventLogEntryType eventLogEntryType)
{
try
{
string message = string.Empty;
string sTrace = ex.StackTrace;
while (ex != null)
{
message = message + Environment.NewLine + Environment.NewLine + ex.Message;
ex = ex.InnerException;
}
message = message + Environment.NewLine + Environment.NewLine + sTrace;
WriteToEventLog(message, eventLogEntryType);
}
catch (Exception ex2)
{
WriteToEventLog(ex2.Message, EventLogEntryType.Error);
}
}
private void WriteToEventLog(string message, EventLogEntryType eventLogEntryType)
{
try
{
this.EventLog.WriteEntry(message, eventLogEntryType);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
}
}
2010-10-20 - NOTE - Added the Service code file. Maybe there is an elementary mistake in here?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
using sysIO = System.IO;
using RivWorks.FeedHandler;
namespace RivWorks.FeedHandler.Service
{
public partial class FeedListener : ServiceBase
{
#region Declarations
private List<string> _keys = new List<string>();
private System.Threading.Timer _clock = null;
private FileSystemWatcher _watcher;
private BackgroundWorker _worker;
static private bool _isBusy = false;
#endregion
#region Constructor
public FeedListener()
{
InitializeComponent();
}
#endregion
#region Start/Stop
protected override void OnStart(string[] args)
{
try
{
_keys.AddRange(new string[] { "csv", "xml", "zip", "rivx" });
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorkerProgressChanged);
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerRunWorkerCompleted);
_watcher = new FileSystemWatcher(AppSettings.Default.FTPRootPath, "*.*");
_watcher.IncludeSubdirectories = true;
_watcher.NotifyFilter = sysIO.NotifyFilters.DirectoryName | sysIO.NotifyFilters.FileName | sysIO.NotifyFilters.LastAccess | sysIO.NotifyFilters.CreationTime | sysIO.NotifyFilters.LastWrite;
_watcher.Created += new sysIO.FileSystemEventHandler(fileCreatedOrChanged);
_watcher.Changed += new sysIO.FileSystemEventHandler(fileCreatedOrChanged);
_watcher.EnableRaisingEvents = true;
// check every 5 minutes...
_clock = new System.Threading.Timer(Tick, null, 0, 300000);
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
this.Stop();
}
}
protected override void OnStop()
{
try
{
_watcher.Dispose();
_watcher = null;
_clock.Dispose();
_clock = null;
_worker.Dispose();
_worker = null;
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
#region Event Handlers
void fileCreatedOrChanged(object sender, sysIO.FileSystemEventArgs e)
{
try
{
DTO.BackgroundWorkerEventArgs eventArgs = new DTO.BackgroundWorkerEventArgs();
sysIO.WatcherChangeTypes myType = e.ChangeType;
bool isValid = false;
foreach (string key in _keys)
{
if (Path.GetExtension(e.FullPath).Replace(".", "").Equals(key, StringComparison.CurrentCultureIgnoreCase))
isValid = true;
}
if (isValid)
{
eventArgs.PathAndFile = e.FullPath;
eventArgs.Key = Path.GetExtension(e.FullPath).ToLower().Replace(".", "");
eventArgs.FileName = Path.GetFileName(e.FullPath);
eventArgs.Path = Path.GetDirectoryName(e.FullPath);
eventArgs.UserName = Path.GetDirectoryName(e.FullPath).Replace(AppSettings.Default.FTPRootPath, "").Replace("\\", "");
eventArgs.IsRunning = true;
System.Threading.Thread.Sleep(30000);
// Now, spin up a new thread and do the work on the file, based on file type...
_isBusy = true;
_worker.RunWorkerAsync(eventArgs); // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) //
int i = 0;
DateTime sTime = DateTime.Now;
DateTime eTime = DateTime.Now;
while (_isBusy)
{
System.Threading.Thread.Sleep(5000);
i++;
}
eTime = DateTime.Now;
TimeSpan ts = new TimeSpan(eTime.Ticks - sTime.Ticks);
string msg = String.Format("Import for {0} started at {1} and ended at {2}. It took {3} cycles and the elapsed time was {4}:{5}:{6}.", eventArgs.UserName, sTime, eTime, i, ts.Hours, ts.Minutes, ts.Seconds);
WriteToEventLog(msg, EventLogEntryType.Information);
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
#region Do work on another Thread
void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
try
{
RivWorks.FeedHandler.Handler handler = new RivWorks.FeedHandler.Handler();
BackgroundWorker bw = sender as BackgroundWorker;
handler.Execute(bw, e);
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
finally
{
_isBusy = false;
}
}
void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
try
{
if (e.ProgressPercentage >= 100)
{
_isBusy = false;
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Cancelled)
{
WriteToEventLog("Cancelled.", EventLogEntryType.Warning);
if (e.Error != null)
{
WriteToEventLog(e.Error, EventLogEntryType.Error);
}
}
else if (e.Error != null)
{
WriteToEventLog(e.Error, EventLogEntryType.Error);
}
else
{
WriteToEventLog("Successfully completed.", EventLogEntryType.Information);
}
_isBusy = false;
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
#region Private Methods
private void Tick(object data)
{
try
{
foreach (string key in _keys)
{
List<string> files = Directory.GetFiles(Path.Combine(AppSettings.Default.FTPRootPath), "*." + key, SearchOption.AllDirectories).ToList();
foreach (string fileName in files)
{
System.Threading.Thread.Sleep(5000);
if (File.Exists(fileName))
{
DateTime lat = File.GetLastWriteTime(fileName);
try
{
File.SetLastWriteTime(fileName, DateTime.Now);
}
catch
{
// just catch and ignore with a short pause...
System.Threading.Thread.Sleep(5000);
}
}
}
}
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
private void WriteToEventLog(Exception ex, EventLogEntryType eventLogEntryType)
{
try
{
string message = string.Empty;
string sTrace = ex.StackTrace;
while (ex != null)
{
message = message + Environment.NewLine + Environment.NewLine + ex.Message;
ex = ex.InnerException;
}
message = message + Environment.NewLine + Environment.NewLine + sTrace;
this.EventLog.WriteEntry(message, eventLogEntryType);
}
catch (Exception ex2)
{
WriteToEventLog(ex2, EventLogEntryType.Error);
}
}
private void WriteToEventLog(string message, EventLogEntryType eventLogEntryType)
{
try
{
this.EventLog.WriteEntry(message, eventLogEntryType);
}
catch (Exception ex)
{
WriteToEventLog(ex, EventLogEntryType.Error);
}
}
#endregion
}
}
Even though it's running as a release exe, you'll still be given the option to attach to a debugger when the app crashes... you just won't see debug symbols, just assembly :)
I believe it's the Dr. Watson process that catches app errors for debugging... Because your app is a service, Dr. Watson can't interact with the desktop, giving you the error you see. You can go to the service properties and mark "allow service to interact with the desktop", found on the LogOn tab, which should then give you a Dr. Watson popup when the app crashes.
Steps to disable Dr. Watson are here:
http://support.microsoft.com/kb/188296
If you want to debug the app on the server, you can enable remote debugging on the server, and attach Visual Studio to the process... if you want to try this, I can give you more tips for debugging a windows service remotely.
HTH,
James
* Edit *
Based on the code you provided, I'd look at the following areas:
Is AppSettings.Default.FTPRootPath set correctly in App.Config?
Are there changes happening to that directory immediately when the service starts? You have a timer commented as "check every five minutes", which is a little confusing, because the FileSystemWatcher will start receiving events as soon as you set EnableRaisingEvents to true. So the issue could actually lie within fileCreatedOrChanged
Along those lines, you have one BackgroundWorker servicing multiple events, and worse, you're firing the handler asynchronously. This is my most likely suspect, because if you call _worker.RunWorkerAsync() again while the first job is running, you'll get an InvalidOperationException. Though I'm not sure why you wouldn't see that in the log
You're using the timer to update the last write time for all files in the watched directory, and you do this every five seconds. This seems like a very bad idea... I'm not sure what you're trying to accomplish. This will fire your FileSystemWatcher's changed event, which would explain why you're crashing less than 10 seconds after you start (the timer's initial tick is set to fire immediately, meaning five seconds later you're changing all the file times, triggering the FileSystemWatcher multiple times shortly after that)
So my best guess is that within five seconds, you've begun firing multiple RunWorkAsync() calls on the same BackgroundWorker, which is a no-no : )
Setting the static variable _isBusy to true/false isn't reliable because you're multi-threading with the BackgroundWorkers... you need to use a Mutex or some other lock, but doesn't that really defeat the purpose of using a BackgroundWorker?
Also, if you wanted to use something like an isBusy flag, it would have to look more like:
while (_isBusy) {
System.Threading.Thread.Sleep(5000);
}
_isBusy = true;
_worker.RunWorkerAsync(eventArgs);
You need _isBusy to be false before you try to launch the Background worker... the way you have it, if the event fires 100 times, you'll make 100 calls.
The easiest solution to your problem would be to create a new BackgroundWorker in the fileCreatedOrChanged method every time the event fires... there's overhead involved in creating so many new threads, but if the work being done in this method is significant, it will be worth the overhead.
You might be able to rely on the built-in BackgroundWorker.IsBusy property, but again, I'd have to question the benefit of asynchronous threading if you're just going to block until the background worker completes.
** Edit **
I understand now what you're trying to accomplish with the initial file timestamp changes... I think you would do better to leave the timestamps alone, but just run through a startup loop to process existing files. You can spawn a background worker thread for each one, just like you do on the FileSystemWatcher nofications. The way you're handling it is deliberately creating a side-effect to trigger the result you want.
I'm losing track a little bit in the growing complexity... the whole queue/dequeue thing might be unnecessary. Or maybe I just am not seeing a need that is truly there. Again, what strikes me is that you are launching the background worker asynchronously, then putting the main thread to sleep until it finishes.
When you put the main thread to sleep, no events will get processed, so you are truly throttling the multi-threading to one thread. I see that you want to write to the event log how long it took for a thread to finish. I will start a second answer to address that if I get a chance to, but the gist of it is to pass a Stopwatch class (which will give you an accurate count of either milliseconds or CPU ticks that pass during an operation) to the DoWorkEventArgs.Result property.
But the code you requested! Basically, wherever you decide to call _worker.RunWorkerAsync(queuedFile), rather than run one class-level BackgroundWorker create a new one each time. Pass all the same parameters for the event handlers, etc. Your service entry point would drop all the BGW references and look like:
protected override void OnStart(string[] args)
{
try
{
_keys.AddRange(new string[] { "csv", "xml", "zip", "rivx" });
_watcher = new FileSystemWatcher(AppSettings.Default.FTPRootPath, "*.*");
_watcher.IncludeSubdirectories = true;
_watcher.NotifyFilter = sysIO.NotifyFilters.DirectoryName | sysIO.NotifyFilters.FileName | sysIO.NotifyFilters.LastAccess | sysIO.NotifyFilters.CreationTime | sysIO.NotifyFilters.LastWrite;
_watcher.Created += new sysIO.FileSystemEventHandler(fileCreatedOrChanged);
_watcher.Changed += new sysIO.FileSystemEventHandler(fileCreatedOrChanged);
_watcher.EnableRaisingEvents = true;
WriteToEventLog("Exit Start", EventLogEntryType.Information);
}
and the code where you run the BGW asynchronously would become:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork);
worker.ProgressChanged += BackgroundWorkerProgressChanged; // Note you don't need
worker.RunWorkerCompleted += BackgroundWorkerRunWorkerCompleted; // the 'new' here
worker.RunWorkerAsync(queuedFile); // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) //
The error message tells you that it couldn't attach a debugger to let you inspect the exception. This is completely unrelated to the fact that this is a release build. Release build and debug builds can both be debugged (fortunately!).
Debugging services is a little different from debugging regular applications. Please check this guide for some advice.