Reputation: 1898
I am making a program that import emails from txt files and send a message to them , but i am facing a problem , currently i am using a thread for the sending mail method to prevent the program to stop responding , the exact problem title is:
Invalid operationexception was handeled >>> Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.
here is the code
int success = 0;
int failed = 0;
int total = 0;
bool IsRunning;
List<string> list = new List<string>();
private void addmails()
{
string path = textBox2.Text;
foreach (string line in File.ReadAllLines(path))
{
list.Add(line);
}
IsRunning = true;
}
private void sendmails(object sender, DoWorkEventArgs e)
{
if (IsRunning == true)
{
if (checkBox1.Checked != true)
{
SmtpClient client = new SmtpClient(comboBox1.Text);
client.Credentials = new NetworkCredential(textBox6.Text, textBox7.Text);
MailMessage message = new MailMessage();
message.From = new MailAddress(textBox3.Text, textBox1.Text);
message.Subject = textBox4.Text;
//message.Body = richTextBox1.Text;
if (textBox5.Text != "")
{
message.Attachments.Add(new Attachment(textBox5.Text));
}
foreach (string eachmail in list)
{
if (IsRunning == true)
{
try
{
message.To.Add(eachmail);
client.Send(message);
listBox1.Items.Add("Successfully sent the message to : " + eachmail);
success++;
}
catch
{
listBox1.Items.Add("Failed to send the message to : " + eachmail);
failed++;
}
message.To.Clear();
total++;
Thread.Sleep(15);
label18.Text = total.ToString();
label19.Text = success.ToString();
label21.Text = failed.ToString();
}
else
{
break;
}
}
IsRunning = false;
button3.Text = "Send";
}
}
}
private void button3_Click(object sender, EventArgs e)
{
if (button3.Text == "Send")
{
tabControl1.SelectedTab = tabPage3;
button3.Text = "Stop";
addmails();
// IsRunning = true;
Thread t2 = new Thread(sendmails); // when using that thread i get a cross threading error
t2.Start();
}
else
{
IsRunning = false;
button3.Text = "Send";
MessageBox.Show("Sending Mails Operation has been terminated","Abort",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
Upvotes: 0
Views: 1017
Reputation:
The problem is that UI (WinForms) controls are being accessed from a non-UI thread (the newly spawned one).
Don't do that. Either Control.Invoke/BeginInvoke
or SynchronizationContext.Post/Send
can be used, depending upon exactly what is required. Looking up this issue using the keywords above will lead to many examples.
Another alternative is to use a BackgroundWorker
object and a the RunWorkCompleted
/ProgressChanged
events and pass in all the required information before sending anything. (E.g. don't "read" the UI in the new thread.)
Happy coding.
Resources / additional reading:
How do I change a ComboBox data from another thread? - Small example of Invoke/BeginInvoke including a nice little wrapper for it. All that is "needed" to solve the problem in the post.
What's up with BeginInvoke? - Discusses restrictions with WinForms control access, how windows messaging works, and the use of Invoke/BeginInvoke. A very good read.
Understand SynchronizationContext - Similar to the previous, but focuses on using SC and not Invoke/BeginInvoke. Only read after previous link.
BackgroundWorker and SynchronizationContext - I prefer to use SynchronicationContext over Control.Invoke/BeginInvoke (I like the interface better and there is no need to check InvokedRequired
or similar). This discusses SC in context of a BGW. Being from 2005, it uses the old "delegate" syntax. If using SC, make sure to use the SC obtained from the UI thread. See Why is SynchronizationContext.Current null? In my applications I often have static Send/Post
wrapper methods that take care of all the details.
Upvotes: 5
Reputation: 16162
You are using accessing UI members from a different thread rather than the thread that they are created on, you have to use Control.Invoke
whenever you want to access control
members or method from another thread.
So, to get this to work you have to "I really would not do this but just answering your question":
if (IsRunning == true)
{
bool checkbox1Checked;
string textBox6Text;
string textBox7Text;
string textBox3Text;
string textBox1Text;
string textBox4Text;
string richTextBox1Text;
string textBox5Text;
MethodInvoker getValues = new MethodInvoker(delegate()
{
checkbox1Checked = checkbox1.Checked;
textBox6Text = textBox6.Text;
textBox7Text = textBox7.Text;
textBox3Text = textBox3.Text;
textBox1Text = textBox1.Text;
textBox4Text = textBox4.Text;
richTextBox1Text = richTextBox1.Text;
textBox5Text = textBox5.Text;
});
if (this.InvokeRequired)
{
this.Invoke(getValues);
}
else
{
getValues();
}
if (checkBox1Checked != true)
{
SmtpClient client = new SmtpClient(comboBox1Text);
client.Credentials = new NetworkCredential(textBox6Text, textBox7Text);
MailMessage message = new MailMessage();
message.From = new MailAddress(textBox3Text, textBox1Text);
message.Subject = textBox4Text;
//message.Body = richTextBox1Text;
if (textBox5Text != "")
{
message.Attachments.Add(new Attachment(textBox5Text));
}
foreach (string eachmail in list)
{
if (IsRunning == true)
{
try
{
message.To.Add(eachmail);
client.Send(message);
MethodInvoker addToListBox = new MethodInvoker(delegate()
{
listBox1.Items.Add("Successfully sent the message to : " + eachmail);
});
if (listBox1.InvokeRequired)
{
listBox1.Invoke(addToListBox);
}
else
{
addToListBox();
}
success++;
}
catch
{
MethodInvoker addToListBox = new MethodInvoker(delegate()
{
listBox1.Items.Add("Failed to send the message to : " + eachmail);
});
if (listBox1.InvokeRequired)
{
listBox1.Invoke(addToListBox);
}
else
{
addToListBox();
}
failed++;
}
message.To.Clear();
total++;
Thread.Sleep(15);
MethodInvoker updateSatatus = new MethodInvoker(delegate()
{
label18.Text = total.ToString();
label19.Text = success.ToString();
label21.Text = failed.ToString();
});
if (this.InvokeRequired)
{
this.Invoke(updateSatatus);
}
else
{
updateSatatus();
}
}
else
{
break;
}
}
IsRunning = false;
if (button3.InvokeRequired)
{
button3.Invoke(new MethodInvoker(delegate() { button3.Text = "Send"; } ));
}
else
{
button3.Text = "Send";
}
}
}
Upvotes: 2