【问题标题】:Program freezing when trying to send emails with smtp尝试使用 smtp 发送电子邮件时程序冻结
【发布时间】:2018-02-25 16:58:43
【问题描述】:

我的程序目前遇到了一个严重问题,我正在尝试学习如何使用 smtp 发送电子邮件,我编写了这段代码来做到这一点。当我尝试单击 button1 时,程序冻结并且没有任何反应。 PS:显然,我在发布之前将电子邮件和密码更改为“电子邮件”和“密码”。 PS2:我正在用葡萄牙语编写代码,因此,您可能不理解的任何内容都可以视为变量或“x”。

代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Mail;
using MySql.Data.MySqlClient;
using System.Net;

namespace Inicio
{
    public partial class Email : Form
    {
        MySqlConnection con = new MySqlConnection(@"Data Source=localhost;port=3306;Initial Catalog=digital wallet;User ID=root;password=");
        public Email()
        {
            InitializeComponent();
        }

        private void SendEmail()
        {

            if (textBox1.Text == "" || textBox2.Text == "")
            {
                MessageBox.Show("Preencha todos os campos", "Erro",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                int i = 0;
                con.Open();
                MySqlCommand cmd = con.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select EMAIL from conta where EMAIL = @email  and LOGIN = @login ";
                cmd.Parameters.AddWithValue("@email", textBox2.Text);
                cmd.Parameters.AddWithValue("@login", textBox1.Text);
                cmd.ExecuteNonQuery();
                DataTable dt = new DataTable();
                MySqlDataAdapter da = new MySqlDataAdapter(cmd);
                da.Fill(dt);
                i = Convert.ToInt32(dt.Rows.Count.ToString());

                if (i == 0)
                {
                    MessageBox.Show("Login ou email inválidos", "Erro",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    cmd.CommandText = "select * from CONTA where LOGIN = @login";
                    cmd.ExecuteNonQuery();

                    string senha = "";
                    string email = "";

                    MySqlDataReader reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        senha = reader.GetString("SENHA");
                        email = reader.GetString("EMAIL");
                    }

                    reader.Close();

                    using (SmtpClient smtp = new SmtpClient())
                    {
                        smtp.Host = "outlook.com";
                        smtp.UseDefaultCredentials = false;
                        NetworkCredential netCred = new NetworkCredential("email", "password");
                        smtp.Credentials = netCred;
                        smtp.EnableSsl = true;

                        using (MailMessage msg = new MailMessage("email", email))
                        {
                            msg.Subject = "Recuperação de senha.";
                            StringBuilder sb = new StringBuilder();
                            sb.AppendLine("A sua senha é atual é: " + senha + Environment.NewLine);
                            sb.AppendLine("Obrigado," + Environment.NewLine);
                            sb.AppendLine("Digital wallet. " + Environment.NewLine);
                            msg.Body = sb.ToString();
                            msg.IsBodyHtml = false;
                            smtp.Send(msg);
                        }
                    }
                }
            }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            SendEmail();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

【问题讨论】:

  • 如果您解决了问题,请发布答案。不要将解决方案编辑到您的问题中。

标签: c# winforms email smtp freeze


【解决方案1】:

更改超时属性。所以,smtp.Timeout = 0;应该可以!

【讨论】:

    【解决方案2】:

    栾特谢拉,

    您应该查看Asynchronous Programming with Async and Await。这将解决您的问题。

    证明我在说什么。看看这段代码。

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private async Task SendEmail()
        {
    
            if (textBox1.Text == "" || textBox2.Text == "")
            {
                MessageBox.Show("Preencha todos os campos", "Erro",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                await Email.SendEmail();
    
                MessageBox.Show("Nice");
            }
        }
        private async void button1_Click(object sender, EventArgs e)
        {
            await SendEmail();
        }
    
        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
    
    public class Email
    {
        public async static Task SendEmail()
        {
            await Task.Delay(15000);
        }
    }
    

    您可以在 textbox1 和 textbox2 中写入内容并单击按钮。等待 15 秒(我正在模拟一个可能需要 15 秒的非常庞大的任务)。同时您可以看到用户界面没有冻结。您可以四处移动窗口。 15 秒后,您将收到一条“Nice”消息。

    那是因为在我的代码中,巨大的任务没有在 GUI 线程中运行。

    希望对你有帮助 亲切的问候 丹尼尔

    【讨论】:

    • 谢谢你,我会看看这个。
    【解决方案3】:

    您在 GUI 环境中运行。事件必须尽快返回,否则 GUI 将被“锁定”。磁盘和网络活动尤其容易长时间运行。您必须应用某种形式的多任务处理(并将长时间运行的操作移到该备用任务中)以避免 GUI 锁定。

    作为初学者,我建议使用 BackgroundWorker 进行多任务处理。 Async...await 更高级。

    编辑:这是我的 WinForms+BackgroundWorker 示例代码。它应该可以帮助您开始正确使用它。

    #region Primenumbers
    private void btnPrimStart_Click(object sender, EventArgs e)
    {
    	if (!bgwPrim.IsBusy)
    	{
    		//Prepare ProgressBar and Textbox
    		int temp = (int)nudPrim.Value;
    		pgbPrim.Maximum = temp;
    		tbPrim.Text = "";
    
    		//Start processing
    		bgwPrim.RunWorkerAsync(temp);
    	}
    }
    
    private void btnPrimCancel_Click(object sender, EventArgs e)
    {
    	if (bgwPrim.IsBusy)
    	{
    		bgwPrim.CancelAsync();
    	}
    }
    
    private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
    {
    	int highestToCheck = (int)e.Argument;
    	//Get a reference to the BackgroundWorker running this code
    	//for Progress Updates and Cancelation checking
    	BackgroundWorker thisWorker = (BackgroundWorker)sender;
    
    	//Create the list that stores the results and is returned by DoWork
    	List<int> Primes = new List<int>();
    	
    
    	//Check all uneven numbers between 1 and whatever the user choose as upper limit
    	for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
    	{
    		//Report progress
    		thisWorker.ReportProgress(PrimeCandidate);
    		bool isNoPrime = false;
    
    		//Check if the Cancelation was requested during the last loop
    		if (thisWorker.CancellationPending)
    		{
    			//Tell the Backgroundworker you are canceling and exit the for-loop
    			e.Cancel = true;
    			break;
    		}
    
    		//Determin if this is a Prime Number
    		for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
    		{
    			if (PrimeCandidate % j == 0)
    				isNoPrime = true;
    		}
    
    		if (!isNoPrime)
    			Primes.Add(PrimeCandidate);
    	}
    
    	//Tell the progress bar you are finished
    	thisWorker.ReportProgress(highestToCheck);
    
    	//Save Return Value
    	e.Result = Primes.ToArray();
    }
    
    private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
    	pgbPrim.Value = e.ProgressPercentage;
    }
    
    private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    	pgbPrim.Value = pgbPrim.Maximum;
    	this.Refresh();
    
    	if (!e.Cancelled && e.Error == null)
    	{
    		//Show the Result
    		int[] Primes = (int[])e.Result;
    
    		StringBuilder sbOutput = new StringBuilder();
    
    		foreach (int Prim in Primes)
    		{
    			sbOutput.Append(Prim.ToString() + Environment.NewLine);
    		}
    
    		tbPrim.Text = sbOutput.ToString();
    	}
    	else 
    	{
    		tbPrim.Text = "Operation canceled by user or Exception";
    	}
    }
    #endregion

    【讨论】:

      【解决方案4】:

      处理您的 sql 查询、连接到 smtp 服务器并将邮件数据传输到服务器需要时间。由于您在 UI 线程中执行所有这些内容,因此您的表单会冻结。

      BackgroundWorker 添加到您的表单,添加.DoWork.RunWorkerCompleted 的事件处理程序,并将SQL 和SMTP 内容添加到DoWork 函数。

      在 Button1Click 上,您使用 .RunWorkerAsync 启动 BackgroundWorker。将一个字符串数组与您的 TextBox1 和 TextBox2 内容一起传递给此函数。您需要这样做,因为您无法从 DoWork 函数访问这些元素。或者,您可以使用.IsBusy 检查工作人员是否已经处于活动状态。启动工作程序后,您应该锁定 UI 或显示一条消息或类似的东西来告诉用户后台进程正在运行。从 Button1Click 函数执行此操作。

      然后修改.DoWork函数,提取传入的字符串数组。为此,您可以访问传递的DoWorkEventArgs.Arguments 并将它们转换为您传递给RunWorkerAsync 的类型 - 转换为字符串数组。交换 TextBox 对数组值的访问。

      RunWorkerCompleted 函数中,您必须解锁 UI。

      开始阅读有关 BackgroundWorker 的信息,您可以使用进度报告功能来通知用户进度,例如“收集 sql 数据,连接到 smtp 服务器,传输邮件数据,完成”...

      【讨论】:

      • 我让程序运行,我收到一个异常,说操作时间限制超过。
      • Operation Time limit Exceeded 意味着网络端出现问题。错误的凭证或协议。找不到服务器。类似的东西。您需要查看内部异常以弄清楚究竟是什么。遇到类似超时的风险是总是将网络内容放入多任务处理的原因之一。
      猜你喜欢
      • 2022-01-18
      • 2014-07-08
      • 1970-01-01
      • 2014-10-02
      • 1970-01-01
      • 2016-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多