我正在制作一个程序,从txt文件导入电子邮件并向他们发送消息,但我面临一个问题,目前我正在使用线程发送邮件方法,以防止程序停止响应,确切的问题标题是:
Invalid operationexception was handeled >>> Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.
这是代码
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);
}
答案 0 :(得分:5)
问题是正在从非UI线程(新生成的线程)访问UI(WinForms)控件。
不要那样做。可以使用Control.Invoke/BeginInvoke
或SynchronizationContext.Post/Send
,具体取决于所需要的内容。使用上述关键字查找此问题会产生许多示例。
另一种方法是在发送任何内容之前使用BackgroundWorker
对象和RunWorkCompleted
/ ProgressChanged
个事件,传入所有必需的信息。 (例如,不要“读取”新线程中的UI。)
快乐的编码。
资源/补充阅读:
How do I change a ComboBox data from another thread? - Invoke / BeginInvoke的一个小例子,包括一个漂亮的小包装器。所有这些都是“需要”来解决帖子中的问题。
What's up with BeginInvoke? - 讨论WinForms控件访问的限制,Windows消息传递的工作原理以及Invoke / BeginInvoke的使用。非常好的阅读。
Understand SynchronizationContext - 与之前类似,但重点是使用SC而不是Invoke / BeginInvoke。只能在上一个链接后阅读。
BackgroundWorker and SynchronizationContext - 我更喜欢在Control.Invoke / BeginInvoke上使用SynchronicationContext(我更喜欢界面,不需要检查InvokedRequired
或类似)。这讨论了BGW背景下的SC。从2005年开始,它使用旧的“委托”语法。如果使用SC,请确保使用从UI线程获取的SC 。请参阅Why is SynchronizationContext.Current null?在我的应用程序中,我经常使用静态Send/Post
包装器方法来处理所有细节。
答案 1 :(得分:2)
您正在使用来自不同线程而不是创建它们的线程的UI成员,只要您想从另一个线程访问Control.Invoke
成员或方法,就必须使用control
。
所以,为了让这个工作,你必须“我真的不会这样做,只是回答你的问题”:
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";
}
}
}