今天公司有一个需求,就是要求搭建一个即时通讯系统,并且要在开机后就要运行,无人值守就可以服务的那种。刚得知这个需求的时候,其实心里就有了办法了,不就是去网上找一个即时通信平台嘛,不光能满足我们的要求,还有优质的服务。开干。。。。过了一会发现,网上好的平台价格还不便宜,不要钱了,可能也不敢用;于是乎,只能自己干了。是不是扯的有点远哈。
好了,今天可能工作的时间有限了,干不完(我比较慵懒),我就先干一部分吧,把后台运行服务搞起来吧。
一、创建Windows Service
1.首先,创建一个Windows 服务:
2、在解决方案资源管理器内将Service1.cs改为QZTService1.cs(这个名称是我项目里的名称,你的项目就随你了),然后点击“查看代码”图标按钮进入代码编辑器界面,如下图所示:
public partial class QZTService1 : ServiceBase
{
public QZTService1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
好了,就完了。服务程序里我现在什么都没干,这个要根据自已的需求来添加代码
3.这一步就是添加服务的安装程序,进入“QZTService1”设计界面,在空白位置右击鼠标弹出上下文菜单,选中“添加安装程序”,如下图所示:
完成后,会自动生成两个组件,分别为“serviceInstaller1”及“serviceProcessInstaller1”,如下图所示:
点击“serviceInstaller1”,在“属性”窗体将ServiceName改为QZTService1,Description改为QZT服务,StartType保持为Manual,如下图所示:
点击“serviceProcessInstaller1”,在“属性”窗体将Account由User改为LocalSystem(服务属性系统级别),如下图所示:
生成项目就算是服务程序写完了(其实什么事情也没干。。),但是还不能收工,因为服务程序是不能直接双击打开运行的;所以我们就要写一点代码来控制它的安装或启动。
二、创建安装、启动、停止、卸载服务的Windows窗体
1.在同一个解决方案里新建一个Windows Form项目,名字你就随便取了。将该项目设置为启动项目,并在窗体内添加四个按钮,分别为安装服务、启动服务、停止服务及卸载服务,我这里用的是一个MenuStrip,和两个Button,如下图所示:
2.接一下就是控制代码了,首先要操作后台服务,必须引入两个类,分别为:“System.ServiceProcess”及“System.Configuration.Install”。
3.代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MHmlg.QZT.Manage.IM
{
public partial class Form1 : Form
{
string serviceFilePath = $"{Application.StartupPath}\\Service\\QZTService1.exe";
string serviceName = "QZTService1";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.VisibleChanged += Form1_VisibleChanged;
}
#region Windows服务,安装及启动操作
//事件:安装服务
private void 安装ToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (this.IsServiceExisted(serviceName)) this.UninstallService(serviceName);
this.InstallService(serviceFilePath);
}
catch { }
}
//事件:卸载服务
private void 卸载ToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (this.IsServiceExisted(serviceName))
{
this.ServiceStop(serviceName);
this.UninstallService(serviceFilePath);
}
}
catch { }
}
//事件:启动服务
private void button1_Click(object sender, EventArgs e)
{
if (this.IsServiceExisted(serviceName)) this.ServiceStart(serviceName);
}
//事件:停止服务
private void button2_Click(object sender, EventArgs e)
{
if (this.IsServiceExisted(serviceName)) this.ServiceStop(serviceName);
}
//判断服务是否存在
private bool IsServiceExisted(string serviceName)
{
ServiceController[] services = ServiceController.GetServices();
foreach (ServiceController sc in services)
{
if (sc.ServiceName.ToLower() == serviceName.ToLower())
{
return true;
}
}
return false;
}
//安装服务
private void InstallService(string serviceFilePath)
{
using (AssemblyInstaller installer = new AssemblyInstaller())
{
installer.UseNewContext = true;
installer.Path = serviceFilePath;
IDictionary savedState = new Hashtable();
installer.Install(savedState);
installer.Commit(savedState);
}
}
//卸载服务
private void UninstallService(string serviceFilePath)
{
using (AssemblyInstaller installer = new AssemblyInstaller())
{
installer.UseNewContext = true;
installer.Path = serviceFilePath;
installer.Uninstall(null);
}
}
//启动服务
private void ServiceStart(string serviceName)
{
using (ServiceController control = new ServiceController(serviceName))
{
if (control.Status == ServiceControllerStatus.Stopped)
{
control.Start();
}
}
}
//停止服务
private void ServiceStop(string serviceName)
{
using (ServiceController control = new ServiceController(serviceName))
{
if (control.Status == ServiceControllerStatus.Running)
{
control.Stop();
}
}
}
#endregion
}
}
打完收工,这个就是创建服务的基本方法,记得运行时要以管理身份运行。如果求调试的话,将服务附加进程到需要调试的项目里面即可。
三、应用程序与Windows服务通信
在应用程序或其他服务中,可以与Windows服务通讯,包括:
- 管理Windows服务的生命期,即开启、停止、暂停和重启服务;
- 获得Windows服务的属性和状态;
- 获得特定计算机上的服务列表;
- 向特定的服务发送命令。
这些操作是通过ServiceController 类完成的。ServiceController是一个可视化控件,可以在工具箱中找到。
比较有意思的是ServiceController 中ExecuteCommand这个方法,调用这个方法,可以向Windows服务发送命令,指挥Windows服务的一些操作。例如,在Windows服务的入口类中有一个复写OnCustomCommand()的方法:
/// <summary>
/// 执行用户自定义消息
/// </summary>
/// <param name="command">消息编号</param>
protected override void OnCustomCommand( int command )
{
try
{
switch( command )
{
case 1: // 业务操作
doBusiness1();
break;
case 2: //业务操作
doBusiness2();
break;
default:
……
break;
}
}
catch( Exception ex )
{
// 错误信息
string strErrorMsg = string.Format("异常:{0}/n", ex.Message );
// 写日志
TLineEventLog.DoWriteEventLog( strErrorMsg, EventType.Error );
// 给管理员发邮件 CMail.SendMail(PropertyManager.strMailFromAddress,PropertyManager.strMailAdminAddress,"","异常信息提示",strErrorMsg );
// 写Trace
Trace.WriteLine( strErrorMsg );
}
}
在另外一个应用程序中通过ServiceController的ExecuteCommand()方法向这个Windows服务发送命令:
myController.ExecuteCommand(2);
Windows服务将执行业务方法:doBusiness2();
应该承认,利用ServiceController与Windows服务通讯的功能目前还十分薄弱。通过ExecuteCommand只能与Windows服务进行简单而有限的通讯。
笔者在实际的应用中,分别用一个命令行程序、一个控制台程序和一个Webservice和Windows服务进行通讯,启动、停止服务,或通过ExecuteCommand控制服务的行为。