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 = ; 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;
}
}
}
在这里我把发送邮件的用户、密码、服务器、端口等都是写死的。实际使用中可以考虑单独建立一张发送邮件的配置表,和邮件信息表关联起来。
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 服务发送邮件的功能就完成了。现在可以把服务开启,然后向数据库插入一条邮件信息的数据,试试看效果。
C# 使用windows服务发送邮件的更多相关文章
- 通过SSIS监控远程服务器Windows服务并发送邮件报警!
利用SSIS不仅可以做BI项目的ETL,而且还可以做一些系统监控和维护工作,由于之前供应商写的Windows服务是读取ESB的消息进行处理,且通过OA流程与访客系统进行了集成,无论是ESB出现状况,还 ...
- asp.net基于windows服务实现定时发送邮件的方法
本文实例讲述了asp.net基于windows服务实现定时发送邮件的方法.分享给大家供大家参考,具体如下: //定义组件 private System.Timers.Timer time; publi ...
- 使用Visual Studio 2015 Community 开发windows服务
昨天研究在.NET下开发Windows服务程序,期间遇到一些小问题,这里仅将自己的开发过程和需要注意的地方写下和广大网友分享…… 1.基础 Windows服务是指系统启动时能够自己运行的程序.W ...
- 利用windows服务+timer或者windows任务计划程序+控制台进行进行每日邮件推送
1.邮件发送代码 using System.Text; using System.Net; using System.Net.Mail; using System.Reflection; using ...
- VS2013开发Windows服务项目
这篇随笔里,我将介绍如何用VS2013开发Windows服务项目,实现的功能是定时发送电子邮件. 开发环境:VS2013,SQL Server2008,采用C#语言开发 步骤一:创建Windows服务 ...
- 子线程导致 Windows 服务停止的情况(Topshelf 结合 Quartz.NET)
Ø 前言 本文主要记录子线程导致 Topshelf 和 Quartz.NET 的 Windows 服务停止的现象,以及使用几种常用子线程的注意事项.因为我们有时可能需要开启多个线程执行复杂的逻辑,如 ...
- 使用 Topshelf 创建 Windows 服务
Ø 前言 C# 创建 Windows 服务的方式有很多种,Topshelf 就是其中一种方式,而且使用起来比较简单.下面使用 Visual Studio Ultimate 2013 演示一下具体的使 ...
- 用 vs 2017创建 windows 服务
转载自:http://www.cnblogs.com/xujie/p/5695673.html 1.新建windows服务项目,我这里选择的是Framework4.0,没有选择高版本是为了防止在服务在 ...
- 快速搭建多线程Windows服务解决方案
一.引言 在软件开发过程中windows服务有的时候非常有用,用于同步数据,发送邮件,宿主WF引擎服务等,但是快速搭建一个好用多线程进行多任务处理的程序往往是一个项目必须考虑的问题.自己在项目中也经常 ...
随机推荐
- MySQL之explain 的type列 & Extra列
explain 可以分析 select 语句的执行,即 MySQL 的“执行计划. 一.type 列 MySQL 在表里找到所需行的方式.包括(由左至右,由最差到最好): | All | inde ...
- jQuery制作简洁的多级联动Select下拉框
今天我们要来分享一款很实用的jQuery插件,它是一个基于jQuery多级联动的省市地区Select下拉框,并且值得一提的是,这款联动下拉框是经过自定义美化过的,外观比浏览器自带的要漂亮许多.另外,这 ...
- css属性image-redering详解
What? image-rendering作为现阶段还处于实验性质中的css属性,他的作用是在浏览器对图片进行比例缩放时,设置其缩放使用的算法,从而来得到我们最终想要的图片结果.而且这个属性可以应用于 ...
- WebGL中图片多级处理(FrameBuffer)
在webgl的使用过程中,我们通常会想对texture进行多级处理并对其贴在表面显示 如对较精准的边缘检测,要先后使用灰度shader.模糊shader.边缘shader来进行处理,而每次的处理对象则 ...
- 经验总结20--C#模拟WEB请求
非常多语言能够使用代码进行WEB请求,获取到须要的数据. 方便调用别人的接口,自己进行处理. HttpWebRequest request = WebRequest.Create(url) as Ht ...
- java对象和json数据转换实现方式3-使用jackson实现
測试代码: package com.yanek.util.json; import java.io.IOException; import java.io.StringWriter; import j ...
- shell脚本学习总结12--系统信号
信号是Linux系统中一种进程通信机制.我们可以使用特定的信号来中断进程.每一种信号都同一个整数值相关联. kill命令可用来想进程发送信号,而trap命令用来处理所接收的信号. kill 列出所有可 ...
- Java实现简单的文件复制
public class FileCopy { public static void main(String[] args) { String path = "d:\\1.txt" ...
- jenkins配置邮件报警
author:headsen chen date: 2018-05-15 13:49:21 在jerkins的主配置页面上: 注意:不用 安装什么报警邮件的插件.直接配置就可以了. 系统管理 --- ...
- 进击的RecyclerView入门三(要是能拖动就好了)
还是接着上一讲"进击的RecyclerView入门二(来点小装饰?)",在上一讲中我们学到了怎么给不同的Item定制不同的外观,但貌似那个蓝色的框实在太丑了,咱还是把它干了吧. @ ...