C# List.Add System.InvalidOperationException - c#

I am handling an event from a child form in its parent form, and when I try adding items from the list contained within the event args of the handler (ScraperForm_SiteScraped in the code below), I am receiving the exception System.InvalidOperationException in my console.
Interestingly enough, it seems to succeed on the first add, but no subsequent attempts.
public partial class ProxyTesterView : UserControl
{
private BindingList<Proxy> proxies = new BindingList<Proxy>();
private BindingList<ProxyJudge> pudges = new BindingList<ProxyJudge>();
private BindingList<ProxyTest> tests = new BindingList<ProxyTest>();
private PauseOrCancelTokenSource pcts = new PauseOrCancelTokenSource();
private ProxyScraperForm scraperForm = new ProxyScraperForm();
public ProxyTesterView()
{
InitializeComponent();
proxies.ListChanged += Proxies_ListChanged;
scraperForm.SiteScraped += ScraperForm_SiteScraped;
}
private void Proxies_ListChanged(object sender, ListChangedEventArgs e)
{
ProxiesDataGridView.RowCount = proxies.Count;
}
private void AddFromScraperToolStripMenuItem_Click(object sender, EventArgs e)
{
scraperForm.Show();
}
private void ScraperForm_SiteScraped(object sender, SiteScrapedEventArgs e)
{
foreach (var proxy in e.ScrapedProxies)
{
proxies.Add(proxy);
}
}
}
Child Form
public partial class ProxyScraperForm : Form
{
private BindingList<IProxyScraperSite> sites = new BindingList<IProxyScraperSite>();
public int ScrapeInterval { get; set; } = 60000;
public event EventHandler<SiteScrapedEventArgs> SiteScraped;
public ProxyScraperForm()
{
InitializeComponent();
sites.Add(new ProxyScraperSiteUsProxyOrg());
sites.Add(new ProxyScraperSiteFreeProxyListNet());
sites.Add(new ProxyScraperSiteFreeProxyListsNet());
sites.Add(new ProxyScraperSiteHideMyName());
sites.Add(new ProxyScraperSiteHidester());
ScraperDataGridView.DataSource = sites;
}
private void ScrapeButton_Click(object sender, EventArgs e)
{
foreach (var site in sites)
{
Task.Run(async () =>
{
while (true)
{
var driver = SeleniumUtility.CreateDefaultFirefoxDriver();
var newProxies = await site.ScrapeAsync(driver);
driver.Quit();
OnSiteScraped(newProxies);
await Task.Delay(5000);
site.Status = $"Waiting {ScrapeInterval / 1000} seconds...";
await Task.Delay(ScrapeInterval);
}
});
}
}
private void OnSiteScraped(List<Proxy> scrapedProxies)
{
if (SiteScraped != null)
{
SiteScraped(this, new SiteScrapedEventArgs(scrapedProxies));
}
}
}

From our comments, turns out that this was a threading issue. As a good practice, always use a try/catch block when there's a chance that an exception can occur in a block of code. :)
Also, if you're using Visual Studio, you can make VS break on more exceptions by pressing CTRL+ALT+E and selecting the checkboxes. You can read more about exception breaking here.

Related

Multiple class instances raising same event

I'm having trouble figuring out what is wrong with my C# code.
I'm trying to learn how to use ConcurrentQueue class in System.Collections.Concurrent namespace.
In order to do this, I'm creating 2 instances of the same class in different threads, passing to the constructors a different Listbox control.
I am expecting each class instance of EventGenerator to raise events at random intervals, updating the Listbox the were passed with randomly generated number, and adding that number to a ConcurrentQueue which is also passed to the constructor.
In my main thread, is the method to DeQueue the ConcurrentQueue of objects EnQueued to it by both spawned threads.
But what I'm getting is the 2 EnQueue Listboxes displaying the same data and the DeQueue Listbox seeming reporting to have deQueued them both.
I apologize if my description is not good enough, and my code follows, along with a link to an image of my form in case it might better help visualize what I'm trying to do...
Form
public partial class Form1 : Form
{
ConcurrentQueue<int> CQ;
EventGenerator eventGenerator1;
EventGenerator eventGenerator2;
public Form1()
{
InitializeComponent();
CQ = new ConcurrentQueue<int>();
eventGenerator1 = new EventGenerator(CQ, listBox1);
eventGenerator1.OnRandomEvent += new EventGenerator.RandomEventHandler(RandomEvent);
eventGenerator2 = new EventGenerator(CQ, listBox2);
eventGenerator2.OnRandomEvent += new EventGenerator.RandomEventHandler(RandomEvent);
}
private void RandomEvent(object sender, IncomingConnectionEventArgs e)
{
string s = e.Property_Int.ToString()
+ " "
+ e.Property_String;
UpdateListbox(s, e.LB);
}
private void UpdateListbox(string argstring, ListBox argListBox)
{
if (InvokeRequired)
{
Invoke(new Action<string, ListBox>(UpdateListbox), new object[] { argstring, argListBox });
return;
}
int n;
bool b = false;
//do
//{
b = CQ.TryDequeue(out n);
//} while (!b);
argListBox.Items.Add(argstring);
argListBox.SelectedIndex = argListBox.Items.Count -1;
listBoxDeQueue.Items.Add(n.ToString());
listBoxDeQueue.SelectedIndex = listBoxDeQueue.Items.Count - 1;
}
private void button_Start_Click(object sender, EventArgs e)
{
Thread methodThread1 = new Thread(new ThreadStart(TheThread1));
methodThread1.Start();
Thread methodThread2 = new Thread(new ThreadStart(TheThread2));
methodThread2.Start();
}
private void TheThread2()
{
eventGenerator2.Start();
}
private void TheThread1()
{
eventGenerator1.Start();
}
private void button_Stop_Click(object sender, EventArgs e)
{
eventGenerator1.Stop();
eventGenerator2.Stop();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
eventGenerator1.Stop();
eventGenerator2.Stop();
}
}
IncomingConnectionEventArgs
class IncomingConnectionEventArgs : EventArgs
{
public System.Windows.Forms.ListBox LB;
public int Property_Int { get; set; }
public string Property_String { get; set; }
public IncomingConnectionEventArgs(int argInt, string argString,
System.Windows.Forms.ListBox lb)
{
LB = lb;
Property_Int = argInt;
Property_String = argString;
}
}
EventGenerator
class EventGenerator
{
public delegate void RandomEventHandler(
object sender,
IncomingConnectionEventArgs e);
public event RandomEventHandler OnRandomEvent;
public Random r = new Random();
public ListBox listBox;
public bool Generate = true;
public ConcurrentQueue<int> CQ;
public EventGenerator(ConcurrentQueue<int> argCQ, ListBox argListBox)
{
CQ = argCQ;
listBox = argListBox;
}
public void Start()
{
Generate = true;
while (Generate)
{
Thread.Sleep(r.Next(100, 2000));
RandomEvent();
}
}
public void Stop()
{
Generate = false; ;
}
public void RandomEvent()
{
if (OnRandomEvent == null)
{
return;
}
int n = r.Next(1000, 10000);
CQ.Enqueue(n);
IncomingConnectionEventArgs Args =
new IncomingConnectionEventArgs(n, "", listBox);
OnRandomEvent(this, Args);
}
}
The problem is with your use of Random. Unless you use a single instance of Random or explicitly seed each instance differently, the two threads calling Random.Next(...) will typically generate the same values.
A better way to seed your instance is this:
Random r = new Random(Guid.NewGuid().GetHashCode());

Can't control Threads from different Methods WPF C#

I'm writing a small Keylogger for some statistics about my typing.
The Keylogger works fine, but now i want to implement it to a wpf to have a better control.
public MainWindow()
{
InitializeComponent();
Thread ThreadLog = new Thread(Log);
Thread ThreadRefreshForm = new Thread(refreshForm);
Thread ThreadAutoSave = new Thread(AutoSave);
ThreadLog.Start();
ThreadRefreshForm.Start();
ThreadAutoSave.Start();
}
private void btn_ThreadLogStop_Click(object sender, RoutedEventArgs e)
{
if (ThreadLog.IsAlive == true)
{
ThreadLog.Abort();
}
This gives me an Error # ThreadLog.IsAlive. How can i solve the Problem?
Thanks for your help!!!!
You should declare your ThreadLog somewhere else and initialize it in the constructor, such that the method can access the ThreadLog:
private Thread ThreadLog;
public MainWindow()
{
InitializeComponent();
ThreadLog = new Thread(Log);
...
}
private void btn_ThreadLogStop_Click(object sender, RoutedEventArgs e)
{
if (ThreadLog.IsAlive == true)
{
ThreadLog.Abort();
}
Generally speaking the correct way how to end threads is like this
private volatile bool m_Stop;
public void ThreadLoop()
{
while(!m_Stop) {
// do some work
}
}
// starting
new Thread(ThreadLoop).Start();
// "force" end
m_Stop = true;
Or if you prefer tasks over threads (which I do):
public void ThreadLoop(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// do some work
}
}
var cancelation = new CancellationTokenSource();
// starting
new Task(() => ThreadLoop(cancelation.Token), cancelation.Token).Start();
// "force" end
cancelation.Cancel();

Silverlight ASync call of Service overriding class member

I'm using silverlight to access a webservice to request some data. This call is asynchronous. I (think I) have to put this data in a class member after doing some operations on it, so I can access it later.
public class CardPrinter
{
// The card to be printed
private UIElement printCard;
public void PrintStaffCard(string p_persoons)
{
Debug.WriteLine(p_persoons);
foreach (string persoon in p_persoons.Split(','))
{
int p_persoon = Convert.ToInt32(persoon.Trim());
this.GetStaffData(p_persoon);
}
}
private void GetStaffData(int p_persoon)
{
PictureServiceClient proxy = new PictureServiceClient();
proxy.GetPersonelCardInfoCompleted += this.Proxy_GetPersonelCardInfoCompleted;
proxy.GetPersonelCardInfoAsync(p_persoon);
}
private void Proxy_GetPersonelCardInfoCompleted(object sender, GetPersonelCardInfoCompletedEventArgs e)
{
if (e.Error != null)
{
Debug.WriteLine(e.Error.Message);
}
else
{
this.SendStaffCardToPrinter(e.Result);
}
}
private void SendStaffCardToPrinter(CardInfo.CardInfo card)
{
Canvas canvas = new Canvas()
//Do some stuff
this.printCard = canvas;
PrintDocument pd = new PrintDocument();
pd.PrintPage += new EventHandler<PrintPageEventArgs>(this.Pd_PrintPage);
pd.Print(card.accountNr, null, true);
}
private void Pd_PrintPage(object sender, PrintPageEventArgs e)
{
e.PageVisual = this.printCard;
}
}
The problem is in the printCard variable. Sometimes it still contains the data from a previous async call in the foreach.
If I could make sure that the call in the foreach is compeletely finished there would not be a problem, but not sure how to do this and if this is the correct way to handle this.
What is the best way to handle a situation like this?
You can make the code easier to use by using TaskCompletionSource to convert the asynchronous methods from event based to task based. Then you can get rid of the variable and usage of the methods becomes much like using a synchronous method.
I haven't tested this, but it should be close to what you need. You may also find the following article useful. And also the following post Nested Asynchronous function in Silverlight
public class CardPrinter
{
public void PrintStaffCard(string p_persoons)
{
Debug.WriteLine(p_persoons);
foreach (string persoon in p_persoons.Split(','))
{
int p_persoon = Convert.ToInt32(persoon.Trim());
var cardInfo = await this.GetStaffDataAsync(p_persoon);
await this.SendStaffCardToPrinterAsync(cardInfo);
}
}
private Task<CardInfo.CardInfo> GetStaffDataAsync(int p_persoon)
{
var tcs = new TaskCompletionSource<CardInfo.CardInfo>();
PictureServiceClient proxy = new PictureServiceClient();
proxy.GetPersonelCardInfoCompleted += (s, e) =>
{
if (e.Error != null)
{
Debug.WriteLine(e.Error.Message);
tcs.SetException(e.Error);
}
else
{
tcs.SetResult(e.Result);
}
};
proxy.GetPersonelCardInfoAsync(p_persoon);
return tcs.Task;
}
private Task SendStaffCardToPrinterAsync(CardInfo.CardInfo card)
{
Canvas canvas = new Canvas();
//Do some stuff
var tcs = new TaskCompletionSource<object>();
PrintDocument pd = new PrintDocument();
pd.PrintPage += (s, e) =>
{
e.PageVisual = canvas;
tcs.SetResult(null);
};
pd.Print(card.accountNr, null, true);
return tcs.Task;
}
}
The GetPersonalCardInfoAsync method should have an overload where you can pass a UserState argument. You can pass your printCard there when you're making the call and access it later in your Proxy_GetPersonelCardInfoCompleted.
private void GetStaffData(int p_persoon, UIElement printCard)
{
PictureServiceClient proxy = new PictureServiceClient();
proxy.GetPersonelCardInfoCompleted += this.Proxy_GetPersonelCardInfoCompleted;
proxy.GetPersonelCardInfoAsync(p_persoon, printCard);
}
private void Proxy_GetPersonelCardInfoCompleted(object sender, GetPersonelCardInfoCompletedEventArgs e)
{
UIElement printCard = (UIElement)e.UserState;
// do stuff
}

Watch a variable from another thread?

I have a C# library, inside which there is a timer that keeps checking a boolean variable ProcessFinished. ProcessFinished is initialized as false.
What I want is that the main application needs to watch the variable Status from the library, and a message box should display once this ProcessFinished becomes true.
The problem I had is the message box never display if I simple execute the main application, but it displays if I step in the main application.
Here is the timer_tick code in main application:
public Window1()
{
_fl = new FijiLauncherControl();
this._statusTimer = new System.Windows.Forms.Timer(); // read log 4 times per sec
this._statusTimer.Interval = 125;
this._statusTimer.Tick += new EventHandler(_statusTimer_Tick);
InitializeComponent();
}
void _statusTimer_Tick(object sender, EventArgs e)
{
try
{
if (_fl.ProcessFinished)
{
System.Windows.MessageBox.Show("Process is finished");
_statusTimer.Stop();
}
}
catch (Exception ex)
{
}
}
private void FijiLaucherButton_Click(object sender, RoutedEventArgs e)
{
_statusTimer.Start();
_fl.LaunchFiji();
}
where the _fl is the object of the class from the other library.
Inside the library, the timer code is like this:
public FijiLauncherControl()
{
_ijmFile = "";
_fijiExeFile = "";
_logFile = "";
_outputDir = "";
_isLogOn = false;
_processOn = false;
_processFinished = false;
_headless = true;
_doneStr = "Procedure is finished.";
_logFileCheckTimer = new System.Timers.Timer(500); // read log 4 times per sec
_logFileCheckTimer.Enabled = true;
_logFileCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(_logFileCheckTimer_Elapsed);
}
void _logFileCheckTimer_Elapsed(object sender, EventArgs e)
{
if (_processOn && IsLogOn)
{
try
{
_processFinished = CheckStatuts();
}
catch (Exception ex)
{
}
}
}
I am wondering what is going on here? Is there anyway I can see the message box shows up without stepping in? What is the right way to watch ProcessFinished from the main application?
Would it not be better to fire an event from the thread and catch it. Then show the message box?
Like this maybe?
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click( object sender, EventArgs e )
{
var logChecker = new LogChecker();
logChecker.FinishedExvent += () => MessageBox.Show( "Finished" );
logChecker.Start();
}
}
internal class LogChecker
{
public void Start()
{
var thread = new Thread( CheckLog );
thread.Start();
}
private void CheckLog()
{
var progress = 0;
while ( progress < 3000 )
{
Thread.Sleep( 250 );
progress += 250;
}
FinishedExvent();
}
public event TestEventHandler FinishedExvent;
}
internal delegate void TestEventHandler();
}
Try
volatile bool _processFinished;

Singleton Bindable Controls While Tasking/Multi-Threading

before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.
The issue is that any bindable control, does not update once the binding list from singleton class is changed. This is a common issue on multi-threaded apps. Most if not all solutions offer suggestions where the bindlinglist or collection is initialized from parent thread, and then some invocation to be made. Not what i'm looking for. The same issue persist if static class is used instead of singleton.
Basically, the application triggers some Tasks, which in turn create object(s) on different business classes. These objects post messages into the bindinglist, which should update the UI listbox, but does not. And yes, the message object is in the list, and binding after the TASK finished works (items displayed). Locking/unlocking object(s) access is also not an issue.
Appreciate any suggestions/solutions
A trimmed down version of business objects:
namespace MyNameSpace
{
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
}
A trimmed down version of another class doing some ops:
namespace MyNameSpace
{
public class WorkDoingClass
{
public WorkDoingClass() { }
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
}
Singleton:
namespace MyNameSpace
{
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public WorkDoingClass() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL};
}
}
}
This is also a trimmed down version from where calls start:
namespace MyNameSpace
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
WorkDoingClass work = new WorkDoingClass();
work.DoSomeOtherWork();
}
Your problem is that the thread(the main UI thread) making the listbox is different from the thread(the worker thread) modifying the collection.
Try the following code. It could solve your issue. I use SynchronizationContext to synchronize the two threads, which serves as the same function with Control.Invoke().
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
WorkDoingClass _work;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
_work = new WorkDoingClass(SynchronizationContext.Current);
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
_work.DoSomeOtherWork();
}
}
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
public class WorkDoingClass
{
private SynchronizationContext _syncContext;
public WorkDoingClass() { }
public WorkDoingClass(SynchronizationContext _syncContext)
{
// TODO: Complete member initialization
this._syncContext = _syncContext;
}
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
_syncContext.Send(DoWork, null);
}
private static void DoWork(object arg)
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public AllMessages() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL; }
}
}
}

Categories