设为首页 收藏本站
查看: 1273|回复: 0

[经验分享] C# 使用windows服务发送邮件

[复制链接]

尚未签到

发表于 2017-12-7 18:30:31 | 显示全部楼层 |阅读模式
  最近做了一个使用 C# 写了一个发送邮件的 windows 服务,在这里记录一下。
  首先使用 Visual Studio 2015 创建一个 windows 服务项目。
DSC0000.png

  然后在设计器上面右击添加安装程序。如下图。
DSC0001.png

  安装好后,选择安装程序设计界面,选择服务和安装程序右击选择属性修改一些属性值。
DSC0002.png

DSC0003.png

DSC0004.png

  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,访问数据库类。


DSC0005.gif DSC0006.gif


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();
}
}
}
  发送成功了。
DSC0007.png

  下面开始安装 windows 服务。
  首先找到路径 C:\Windows\Microsoft.NET\Framework\v4.0.30319 或者路径 C:\Windows\Microsoft.NET\Framework\v2.0.50727 下面的 InstallUtil.exe,具体是哪一个下面的,根据版本而定。
DSC0008.png

  然后新建一个文件夹,把刚刚找到的 InstallUtil.exe 文件和 bin\ Debug 或者 Release 文件夹下编译好的文件全部复制到该文件夹下。
DSC0009.png

  然后以管理员身份运行 cmd,输入如下图命令安装 windows 服务。
DSC00010.png

  使用 InstallUtil.exe SendMail.exe /u 命令卸载安装的服务。
  或者使用下面这种简单的方法。
DSC00011.png

  附上代码:





@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 ,所以需要把服务设置为延迟启动。
DSC00012.png

  选中服务右击,选择属性。把启动类型设置为:自动(延迟启动)。
DSC00013.png

  这样一个使用 windows 服务发送邮件的功能就完成了。现在可以把服务开启,然后向数据库插入一条邮件信息的数据,试试看效果。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-421879-1-1.html 上篇帖子: Apache提示You don't have permission to access / on this server问题解决 下篇帖子: Windows 用bat脚本带配置启动redis,并用vb脚本使其在后台运行。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表