C# 使用windows服務發送郵件


最近做了一個使用 C# 寫了一個發送郵件的 windows 服務,在這里記錄一下。

首先使用 Visual Studio 2015 創建一個 windows 服務項目。

然后在設計器上面右擊添加安裝程序。如下圖。

安裝好后,選擇安裝程序設計界面,選擇服務和安裝程序右擊選擇屬性修改一些屬性值。

PS:如果不給服務添加安裝程序,后面是沒法把服務安裝至 windows 系統里的。

在數據庫創建一個表,用於存儲需要發送的郵件信息。

create table MainInfo
(
    MainInfoID    int  not null identity(1,1) primary key,
    Mail_To  nvarchar(64) not null,    -- 收件人郵箱
    Title nvarchar(128) not null,    -- 郵件標題
    Content nvarchar(max)  null, -- 郵件內容
    Mode int not null default(0), -- 發送方式,0為默認發送,1為抄送,2為密送
    SendState int not null default(0), -- 發送狀態,0為未發送,1為發送成功,2為發送失敗
    IsTimer int not null default(0), -- 0為即時發送,1為定時發送
    SendTime nvarchar(64) null,        -- 定時發送的時間
    AttAchFileUrl nvarchar(max) null,    -- 添加附件的地址
    AttAchFileName nvarchar(128) null,  -- 附件名稱
    IsServerUrl int null default(0)    -- 附件地址是否為服務器地址
)

下面開始貼出代碼:

SqlHelper.cs,訪問數據庫類。

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SendMail
{
    public class Conn
    {
        public static string StrConn
        {
            get
            {
                //讀取文本文件(txt)
                //return Conn.getValue(@"C:\Users\Brambling\Desktop\Demo\SendMail\SendMail\DB_Config\DB_Config.txt", "StrConn");

                //讀取配置文件
                //return ConfigurationManager.ConnectionStrings["StrConn"].ToString();
                //return ConfigurationManager.AppSettings["Conn"].ToString();
                
                //直接返回數據庫連接字符串
                return "Data Source=.;Initial Catalog=Test;User ID=sa;Pwd=xxxxxx;Enlist=true;Pooling=true;Max Pool Size=300;Min Pool Size=0;Connection Lifetime=300;packet size=1000";
            }
        }

        public static SqlConnection SqlConn
        {
            get
            {
                return new SqlConnection(StrConn);
            }
        }

        private static string getValue(string path, string name)
        {
            string[] str = File.ReadAllLines(path);
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i].StartsWith(name))
                {
                    return str[i].Replace(name + "=", "");
                }
            }
            return "";
        }
    }


    public class SqlHelper
    {
        public DataSet GetDataSet(string sql)
        {
            DataSet ds = new DataSet();
            SqlConnection conn = Conn.SqlConn;
            try
            {
                conn.Open();
                SqlDataAdapter sda = new SqlDataAdapter(sql, conn);
                sda.Fill(ds);
            }
            catch (Exception)
            {
            }
            finally
            {
                conn.Close();
            }
            return ds;
        }

        public DataTable GetDataTable(string sql)
        {
            DataSet ds = new DataSet();
            DataTable dt = new DataTable();
            SqlConnection conn = Conn.SqlConn;
            try
            {
                conn.Open();
                SqlDataAdapter sda = new SqlDataAdapter(sql, conn);
                sda.Fill(ds);
                if (ds != null && ds.Tables.Count > 0)
                {
                    dt = ds.Tables[0];
                }
            }
            catch (Exception)
            {
            }
            finally
            {
                conn.Close();
            }
            return dt;
        }

        public bool ExecSql(string sql)
        {
            int num = 0;
            SqlConnection conn = Conn.SqlConn;
            try
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand(sql, conn);
                num = cmd.ExecuteNonQuery();
            }
            catch (Exception)
            {
            }
            finally
            {
                conn.Close();
            }
            return num > 0;
        }
    }
}
SqlHelper

這里我嘗試了讀取配置文件的數據庫連接串,但是好像 windows 服務讀取不到配置文件。還有個辦法就是讀取一個文本文件(txt)。

文本文件(txt)的連接串寫法:

StrConn=Data Source=.;Initial Catalog=Test;User ID=sa;Pwd=xxxxxx;Enlist=true;
Pooling=true;Max Pool Size=300;Min Pool Size=0;Connection Lifetime=300;packet size=1000

Mail.cs,發送郵件類。

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;

namespace SendMail
{
    public class Mail
    {
        SqlHelper sqlhelper = new SqlHelper();

        public void SendMail()
        {
            MailMessage mailmsg = null;
            NetworkCredential credential = null;
            SmtpClient client = null;

            string sql = " select top 1 * from MainInfo where SendState='0' and (IsTimer='0' or (IsTimer='1' and SendTime is not null and Convert(datetime,SendTime)<=getdate())) order by IsTimer,SendTime ";
            DataTable dt = sqlhelper.GetDataTable(sql);
            if (dt != null && dt.Rows.Count > 0)
            {
                string Id = dt.Rows[0]["MainInfoID"].ToString();

                try
                {
                    //創建一個身份憑證,即發送郵件的用戶名和密碼
                    credential = new NetworkCredential("980095349@qq.com", "xxxxxx");

                    //發送郵件的實例,服務器和端口
                    client = new SmtpClient("smtp.qq.com", 25);
                    //發送郵件的方式,通過網絡發送
                    client.DeliveryMethod = SmtpDeliveryMethod.Network;
                    //是否啟用 SSL 
                    client.EnableSsl = true;
                    //指定發送郵件的身份憑證
                    client.Credentials = credential;

                    //發送的郵件信息
                    mailmsg = new MailMessage();

                    // 指定發件人郵箱和顯示的發件人名稱
                    mailmsg.From = new MailAddress("980095349@qq.com", "午夜游魂");

                    // 指定收件人郵箱
                    MailAddress mailto = new MailAddress(dt.Rows[0]["Mail_To"].ToString());
                    if (dt.Rows[0]["Mode"].ToString() == "1")
                    {
                        mailmsg.CC.Add(mailto);     // 抄送
                    }
                    else if (dt.Rows[0]["Mode"].ToString() == "2")
                    {
                        mailmsg.Bcc.Add(mailto);    // 密送
                    }
                    else
                    {
                        mailmsg.To.Add(mailto);     // 默認發送
                    }

                    //郵件主題
                    mailmsg.Subject = dt.Rows[0]["Title"].ToString();
                    mailmsg.SubjectEncoding = Encoding.UTF8;

                    //郵件內容
                    mailmsg.Body = dt.Rows[0]["Content"].ToString();
                    mailmsg.BodyEncoding = Encoding.UTF8;

                    //添加附件
                    string url = dt.Rows[0]["AttAchFileUrl"].ToString();    // 附件地址
                    string name = dt.Rows[0]["AttAchFileName"].ToString();   // 附件名稱
                    if (dt.Rows[0]["IsServerUrl"].ToString() == "1")    // 判斷附件地址是否為服務器地址
                    {
                        if (!string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(name))
                        {
                            // 從指定的服務器附件地址加載附件,並轉換為 IO 流 添加到郵件附件中
                            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                            Stream stream = response.GetResponseStream();

                            mailmsg.Attachments.Add(new Attachment(stream, name));
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(url))
                        {
                            mailmsg.Attachments.Add(new Attachment(@url));   // 本地路徑可直接加載
                        }
                    }

                    client.Send(mailmsg);   // 發送郵件
                    UpdateState(Id, "1");   // 發送成功修改發送狀態為 1
                }
                catch (Exception ex)
                {
                    UpdateState(Id, "2");   // 發送失敗修改發送狀態為 2
                }
            }
        }

        public bool UpdateState(string Id, string state)
        {
            string SendTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

            string sql = " update MainInfo set SendState='" + state + "',SendTime='" + SendTime + "' where MainInfoID='" + Id + "' ";
            bool b = sqlhelper.ExecSql(sql);
            return b;
        }
    }
}
Mail

在這里我把發送郵件的用戶、密碼、服務器、端口等都是寫死的。實際使用中可以考慮單獨建立一張發送郵件的配置表,和郵件信息表關聯起來。

SendMailMain.cs,設置定時器類。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace SendMail
{
    public class SendMailMain
    {
        // 設置定時器
        private System.Timers.Timer t = null;


        public void Init()
        {
            try
            {
                if (t == null)
                {
                    t = new System.Timers.Timer();
                    t.Elapsed += new ElapsedEventHandler(SendMail);     // 綁定事件
                    t.Interval = 5000;      // 指定執行的間隔時間
                    t.Enabled = true;   // 是否啟用執行 System.Timers.Timer.Elapsed 事件
                    t.AutoReset = true;     // 設置為 true 表示一直執行,false 為只執行一次
                }
            }
            catch
            {
                t.Stop();
                t.Dispose();
            }
        }

        private void SendMail(object sender, ElapsedEventArgs args)
        {
            try
            {
                ((System.Timers.Timer)sender).Enabled = false;  //單線程管控
                Mail mail = new Mail();
                mail.SendMail();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                ((System.Timers.Timer)sender).Enabled = true;  //單線程管控
            }
        }
    }
}
SendMailMain

Service1.cs,服務開始類。

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.Threading.Tasks;

namespace SendMail
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();

            // 啟用 暫停和恢復服務功能
            //base.CanPauseAndContinue = true;
        }

        // 服務開始執行方法
        protected override void OnStart(string[] args)
        {
            SendMailMain sm = new SendMailMain();
            sm.Init();
        }

        // 服務停止執行方法
        protected override void OnStop()
        {
        }

        // 計算機關閉執行方法
        protected override void OnShutdown()
        {
        }

        // 恢復服務執行方法
        protected override void OnContinue()
        {
        }

        // 暫停服務執行方法
        protected override void OnPause()
        {
        }

    }
}
Service1

上面就是完全的代碼了,下面先創建一個測試單元,測試一下發送郵件。

首先在數據庫插入一條要發送的郵件信息的數據:

insert into MainInfo(Mail_To,Title,Content,AttAchFileUrl,AttAchFileName,IsServerUrl)
  values('1171588826@qq.com','測試郵件','測試郵件,請勿回復!',
  'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494357502809&di=66d6a7909bfe54624a16e02caefb9838&imgtype=0&src=http%3A%2F%2F5.66825.com%2Fdownload%2Fpic%2F000%2F330%2F7599586ba2ba3bed5d76ea182883fca6.jpg',
  '孫悟空.jpg','1')

然后直接使用測試單元調用發送郵件的方法:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SendMail;
using System.IO;

namespace UnitTest
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            Mail mail = new Mail();
            mail.SendMail();
        }
    }
}

發送成功了。

下面開始安裝 windows 服務。

首先找到路徑 C:\Windows\Microsoft.NET\Framework\v4.0.30319 或者路徑 C:\Windows\Microsoft.NET\Framework\v2.0.50727 下面的 InstallUtil.exe,具體是哪一個下面的,根據版本而定。

 

然后新建一個文件夾,把剛剛找到的 InstallUtil.exe 文件和 bin\ Debug 或者 Release 文件夾下編譯好的文件全部復制到該文件夾下。

 

然后以管理員身份運行 cmd,輸入如下圖命令安裝 windows 服務。

使用 InstallUtil.exe SendMail.exe /u 命令卸載安裝的服務。

 

或者使用下面這種簡單的方法。

附上代碼:

@echo 啟動安裝服務中....
@Set installPath=C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe
@Set serversName=SendMailService
@goto checkFile
:checkFile
    @echo 檢測Framework安裝路徑: %installPath%
    @IF NOT EXIST "%installPath%" GOTO filed 
    @IF EXIST "%installPath%" GOTO success
:run
    @rem 
    @set /p type=請選擇服務操作模式,安裝(1),卸載(2),退出(3)...
    @IF "%type%"==""  goto run
    @IF "%type%"=="1" goto runInstall
    @IF "%type%"=="2" goto runUnInstall
    @IF "%type%"=="3" exit
:success
    @echo 地址檢測完成
    @rem 
    @goto run
:filed
    @echo 檢測失敗,當前路徑文件不存在...
    @set /p installPath=請重新指定物理路徑:
    @goto checkFile
:runInstall
    %installPath% SendMail.exe
    @rem
    @net start %serversName%
    @pause
    
    @goto run
:runUnInstall
    @rem
    @net stop %serversName%
    @sc delete %serversName%
    @pause
    @rem
    @goto run
代碼

把上面這一段代碼復制到新建的文本文件(txt)中,然后把后綴改為 bat,然后把它和 bin\ Debug 或者 Release 文件夾下編譯好的文件放到同一個文件夾下。然后執行這個后綴為 bat 的文件,根據提示的步驟就可以很簡單的安裝和卸載服務了。

 

啟動服務命令: net start SendMailService(服務的名稱)

停止服務命令: net stop SendMailService(服務的名稱)

 

PS:如果安裝的服務的文件進行了修改,但是路徑沒有變化的話是不需要重新注冊服務的,直接停止服務,然后用新的文件覆蓋原來的文件即可,如果路徑發生變化,應該先卸載這個服務,然后重新安裝這個服務。

 

最后一步,因為這個服務依賴於 sql server ,所以需要把服務設置為延遲啟動。

選中服務右擊,選擇屬性。把啟動類型設置為:自動(延遲啟動)。

這樣一個使用 windows 服務發送郵件的功能就完成了。現在可以把服務開啟,然后向數據庫插入一條郵件信息的數據,試試看效果。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM