C#对Windows服务的创建、安装和卸载
开发工具:VS2019
目的:开启一个服务监听端口进行以太网通讯
一.创建 Windows服务项目
1.创建项目
创建成功后,如下图所示
将 Service1.cs 改名为 BridgeService.cs
双击 BridgeService.cs 后如下图所示
2.添加安装程序
在上图中按中鼠标右键,添加安装程序
添加完成后,在项目中自动生成 ProjectInstaller.cs
双击 ProjectInstaller.cs 得到下图界面
在项目中, ServiceProcessInstaller 为每个服务应用程序创建一个实例,并 ServiceInstaller 为应用程序中的每个服务创建一个实例。 在项目安装程序类构造函数中,使用和实例设置服务的安装 ServiceProcessInstaller 属性 ServiceInstaller ,并将实例添加到集合中 Installers 。
3.配置参数
查看 serviceInstaller1 的属性
- ServiceName:表示该服务显示的名称
- StartType:设置为开机自动启动
- Description:表示对服务的描述
4.添加服务相关事件
using System.ServiceProcess;
namespace BService
{
public partial class BridgeService : ServiceBase
{
public BridgeService()
{
InitializeComponent();
}
/// <summary>
/// 启动服务时发生
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
}
/// <summary>
/// 停止服务时发生
/// </summary>
protected override void OnStop()
{
}
}
}
5.编写所需的代码
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BService
{
public partial class BridgeService : ServiceBase
{
public BridgeService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
RunLog.WriteLine("提示:开启服务");
_tokenSource = new CancellationTokenSource();
_tcpClient = new TcpClient();
Task.Factory.StartNew(listen, _tokenSource.Token);
Task.Factory.StartNew(read, _tokenSource.Token);
RunLog.WriteLine("提示:开启监听端口5000和开启接收数据");
}
protected override void OnStop()
{
_tokenSource.Cancel();
RunLog.WriteLine("提示:停止服务");
}
CancellationTokenSource _tokenSource = new CancellationTokenSource();
TcpClient _tcpClient;
void listen()
{
Socket newSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 5000);
try
{
newSocket.Bind(localEP);
newSocket.Listen(10);
while (!_tokenSource.IsCancellationRequested)
{
Socket socket = newSocket.Accept();
EndPoint endPoint = socket.RemoteEndPoint;
IPEndPoint ipPoint = (IPEndPoint)endPoint;
RunLog.WriteLine("已连接到 {0}:{1}", ipPoint.Address, ipPoint.Port);
_tcpClient.Client = socket;
}
}
catch (Exception ee)
{
RunLog.WriteLine("错误:监听异常--{0}", ee.Message);
}
}
void read()
{
try
{
List<byte> listData = new List<byte>();
bool haveData = false;
while (!_tokenSource.IsCancellationRequested)
{
if (_tcpClient.Connected)
{
int length = _tcpClient.Available;
if (length != 0)
{
byte[] bData = new byte[length];
_tcpClient.GetStream().Read(bData, 0, length);
listData.AddRange(bData);
haveData = true;
}
else if (haveData)
{
RunLog.WriteLine("提示:接收到数据:{0}", Encoding.Default.GetString(listData.ToArray()));
listData = new List<byte>();
haveData = false;
}
}
Thread.Sleep(10);
}
}
catch (Exception ee)
{
RunLog.WriteLine("错误:读取异常--{0}", ee.Message);
}
}
}
}
using System;
using System.IO;
using System.Text;
namespace BService
{
public class RunLog
{
static Object _lock = new object();
public static void WriteLine(string format, params object[] args)
{
lock (_lock)
{
string text = format;
if (args.Length != 0)
{
text = string.Format(format, args);
}
string fileName = string.Format(@"D:/BService.log");
text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " " + text;
try
{
FileInfo info = new FileInfo(fileName);
bool isSet = false;
if (info.Exists)
{
info.IsReadOnly = false;
isSet = true;
}
using (StreamWriter sw = new StreamWriter(fileName, true, Encoding.Default))
{
sw.WriteLine(text);
sw.Close();
}
if (isSet)
{
info.IsReadOnly = true;
}
}
catch
{
}
}
}
}
}
二、多服务的创建
在一个项目中添加多个服务,可以跳过
1.添加新服务
在 BService项目中添加新项 (MyService.cs 服务)
2.添加事件
using System.ServiceProcess;
namespace BService
{
public partial class MyService : ServiceBase
{
public MyService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
RunLog.WriteLine("提示:开启服务{0}", "MyService");
}
protected override void OnStop()
{
RunLog.WriteLine("提示:停止服务{0}", "MyService");
}
}
}
3.添加ServiceInstaller
4.配置参数
5.多服务的重点设置
修改Program ,在 ServiceBase 中添加 MyService
using System.ServiceProcess;
namespace BService
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new BridgeService(),
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
}
}
在ProjectInstaller.Designer.cs 中将 serviceInstaller2 添加到 Installers中
修改了这两个地方才能同时安装两个服务
三、启动服务
1.建立操作窗体
另起 Winform 项目 ServiceClient,建立新的窗体
2.添加引用
右键项目添加引用:将BService添加到该项目中 ,以方便调试
3.编写按钮事件
using System;
using System.Collections;
using System.Configuration.Install;
using System.ServiceProcess;
using System.Windows.Forms;
namespace ServiceClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string _serviceFilePath = "BService.exe";
string[] _serviceNames = new string[] { "BridgeService", "MyService" };
//事件:安装服务
private void button1_Click(object sender, EventArgs e)
{
foreach (string name in _serviceNames)
{
if (IsServiceExisted(name))
{
UninstallService(_serviceFilePath);
break;
}
}
InstallService(_serviceFilePath);
}
//事件:启动服务
private void button2_Click(object sender, EventArgs e)
{
foreach (string name in _serviceNames)
{
if (IsServiceExisted(name))
{
ServiceStart(name);
}
}
}
//事件:停止服务
private void button3_Click(object sender, EventArgs e)
{
foreach (string name in _serviceNames)
{
if (IsServiceExisted(name))
{
ServiceStop(name);
}
}
}
//事件:卸载服务
private void button4_Click(object sender, EventArgs e)
{
bool isHave = false;
foreach (string name in _serviceNames)
{
if (IsServiceExisted(name))
{
ServiceStop(name);
isHave = true;
}
}
if (isHave)
{
UninstallService(_serviceFilePath);
}
}
//判断服务是否存在
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();
}
}
}
}
}
4.查看服务
启动后,可以在计算机管理中查看安装的服务和服务当前的状态
四、服务使用的注意点
1.文件路径
服务的本体虽然是在 ServiceClient中,但是在服务中对文件的操作路径不是在 ServiceClient中,所以在服务中若要对文件进行操作,路径最好是绝对路径,如上述服务中的 Log 路径
我的电脑 win7-64位:C:\Windows\SysWOW64
2.服务调试
当服务启动后,如何对服务进行调试呢?
在VS中选择调试中的附加到进程
显示所有用户的进程,找到 BService.exe ,然后点击附加就可以监控和调试 服务代码了
五、总结
同一个项目中有两个服务时,安装可以成功但是在启动时有时会报错:错误1083:配置成在该可执行程序中运行的这个服务不能执行该服务 。原因目前不清楚,所以尽量不要将两个服务放在一个项目中,可以分开单独创建。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?