|
最近做了一个使用 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),
SendState int not null default(0),
IsTimer int not null default(0),
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.StartsWith(name))
{
return str.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 服务发送邮件的功能就完成了。现在可以把服务开启,然后向数据库插入一条邮件信息的数据,试试看效果。 |
|