最近做了一个使用 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 = ; 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 > )
{
dt = ds.Tables[];
}
}
catch (Exception)
{
}
finally
{
conn.Close();
}
return dt;
} public bool ExecSql(string sql)
{
int num = ;
SqlConnection conn = Conn.SqlConn;
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
num = cmd.ExecuteNonQuery();
}
catch (Exception)
{
}
finally
{
conn.Close();
}
return num > ;
}
}
}

SqlHelper

这里我尝试了读取配置文件的数据库连接串,但是好像 windows 服务读取不到配置文件。还有个办法就是读取一个文本文件(txt)。

文本文件(txt)的连接串写法:

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

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 > )
{
string Id = dt.Rows[]["MainInfoID"].ToString(); try
{
//创建一个身份凭证,即发送邮件的用户名和密码
credential = new NetworkCredential("980095349@qq.com", "xxxxxx"); //发送邮件的实例,服务器和端口
client = new SmtpClient("smtp.qq.com", );
//发送邮件的方式,通过网络发送
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[]["Mail_To"].ToString());
if (dt.Rows[]["Mode"].ToString() == "")
{
mailmsg.CC.Add(mailto); // 抄送
}
else if (dt.Rows[]["Mode"].ToString() == "")
{
mailmsg.Bcc.Add(mailto); // 密送
}
else
{
mailmsg.To.Add(mailto); // 默认发送
} //邮件主题
mailmsg.Subject = dt.Rows[]["Title"].ToString();
mailmsg.SubjectEncoding = Encoding.UTF8; //邮件内容
mailmsg.Body = dt.Rows[]["Content"].ToString();
mailmsg.BodyEncoding = Encoding.UTF8; //添加附件
string url = dt.Rows[]["AttAchFileUrl"].ToString(); // 附件地址
string name = dt.Rows[]["AttAchFileName"].ToString(); // 附件名称
if (dt.Rows[]["IsServerUrl"].ToString() == "") // 判断附件地址是否为服务器地址
{
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
}
catch (Exception ex)
{
UpdateState(Id, ""); // 发送失败修改发送状态为 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 = ; // 指定执行的间隔时间
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','')

然后直接使用测试单元调用发送邮件的方法:

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=请选择服务操作模式,安装(),卸载(),退出()...
@IF "%type%"=="" goto run
@IF "%type%"=="" goto runInstall
@IF "%type%"=="" goto runUnInstall
@IF "%type%"=="" 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 服务发送邮件的功能就完成了。现在可以把服务开启,然后向数据库插入一条邮件信息的数据,试试看效果。

最新文章

  1. BZOJ 后缀自动机四&#183;重复旋律7
  2. Net作业调度(五)—quartz.net动态添加job设计
  3. 面向对象架构模式之:领域模型(Domain Model)
  4. mysql注入研究
  5. dirname basename 截取路径中的目录以及文件名
  6. 四种数据持久化方式(下) :SQLite3 和 Core Data
  7. 福昕阅读器drm加密解密总结
  8. js页面loading加载
  9. 有关java 8
  10. 洛谷 [P1801] 黑匣子
  11. LeetCode第七天
  12. 微信开发之SVN提交代码与FTP同步到apache的根目录
  13. 性能优化之mysql优化——慢查日志的开启方式和存储
  14. 实战深度学习(上)OpenCV库
  15. 一文让你秒懂互联网TCP/IP协议的深层含义
  16. Java网络编程的基本网络概念
  17. 安卓开发_浅谈Action Bar
  18. kafka项目中踩到的一个坑(客户端和服务器端版本不一致问题)
  19. django 常用方法总结 &lt; 手写分页-上传头像-redis缓存,排行 ...&gt;
  20. Vs2013 如何使用EF6来连接mysql数据库

热门文章

  1. Makefile foreach(转)
  2. 开发环境eclipse for Mac 下的常用快捷键汇总(基本参照Win系,将Ctrl换为Command)
  3. Docker1.12 + Swarm 构建动态微服务应用
  4. 如何在 Linux 上录制你的终端操作
  5. html 事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。
  6. 160504、springmvc返回json数据编码格式设定
  7. VMware虚拟机下安装CentOS7.0超详细图文教程
  8. CodeForces 732B Cormen — The Best Friend Of a Man
  9. Go学习笔记一:解析toml配置文件
  10. Windows使用中的一些小技巧