Use C# to create and install Windows service programs (dry information)

Development language: C#

Development environment: Visual Studio 2022

Microsoft official documentation:Developing Windows Service Applications - .NET Framework | Microsoft Learn

Recently, the company required the use of Windows services as consumers of message queues, so I studied how to create, use and deploy Windows services in C#. I am a Xigua programmer. I will record this article for your reference and study.

Table of contents

1. Overview of Windows Services

1.1-What is a Windows service?

1.2-What can Windows be used for?

1.3-What are the advantages of Windows services?

2. Create a Windows service

2.1-Create Windows Service Project

2.2 - Methods that services can override

2.3-Configuration log (log4net)

3. Operation and deployment of Windows services

3.1-Basic configuration of service

3.2-Service operation and release

3.3-Common commands

3.4-View computer events

3.5- Install multiple instances of the same Windows service on the server

3.6-Common mistakes

1. Unable to open the Service Control Manager on computer "."

2. Unable to load basic dependencies of file or assembly xxx

3. When executing the [.bat] file, there is no relevant error in the reported path.

4. Efficient tool: write bat to start the uninstall service

4.1-Installation Services

4.2-Start the service

4.3-Stop service

4.4-Uninstall the service


1. Overview of Windows Services

1.1-What is a Windows service?

Windows services are applications that run in the background on the Windows operating system. They can start automatically at system startup and are independent of user login. Windows services are typically used to perform tasks that are long-running, require no user interaction, or require continuous execution in the background.

1.2-What can Windows be used for?

  1. Background tasks and automation: You can use Windows services to perform recurring scheduled tasks, data synchronization, regular backups, report generation, etc.
  2. Network services: Windows services can serve as network servers to provide network services, such as Web servers, FTP servers, mail servers, etc.
  3. Scheduled tasks: Windows services can create scheduled tasks and trigger execution operations at specified intervals or when specific events occur.
  4. Data processing: You can use Windows services to perform batch processing tasks such as data processing, data conversion, and data cleaning.
  5. Message queue: It can be used as a consumer of the message queue. The background task always maintains a long connection with the message queue, and will automatically receive it for business processing when it needs to be consumed.

1.3-What are the advantages of Windows services?

  1. Background execution: Windows services run in the background without interfering with users' work and can continue to perform tasks without requiring users to log in.
  2. System level permissions: Windows services can run at the system level with higher permissions and can access system resources and perform sensitive operations.
  3. Automatic startup: Windows services can automatically start when the system starts, ensuring that tasks are always running.
  4. Reliability and Stability: Windows services are designed as long-running applications with high reliability and stability.

2. Create a Windows service

2.1-Create Windows Service Project

(1) Open the [Visual Studio] development tool, then select [Windows Service (.NET Framework)] and click Next.

Note: Windows services are only available in the .NET Framework version, use Worker Service in cross-platform.

(2) Modify the project name and project storage directory. The project name I wrote is [MyDemoService], and then the framework I choose is [.NET Farmework 4.8]. You can fill in and select this according to your own needs, and then click [Create].

The created directory is as follows: [Program.cs] is the entrance to the main program, and [Service1.cs] is the entrance to the service. You can create multiple ones and then configure them in Prodrams.cs.

(3) The [Service1] service name can be renamed and modified. Here I renamed it to [MyDemoService], and the Program.cs file also needs to be modified accordingly.

(4) Then we can write business logic code in [MyDemoService]. There are many ways to locate the specific file to be written. Let’s list two commonly used ones first.

Method 1: In the [program.cs] file, find this class and press F12 on the keyboard to directly enter the file.

Method 2: Right-click directly and click [View Code].

The business code is written here:

At this point, the service has been created, and then you just need to write the specific business code. Note: Services must override at least OnStart and OnStop to be useful.

2.2 - Methods that services can override

        /// <summary>
        /// 服务启动:指示服务开始运行时应采取的操作。 必须在此过程中为服务编写代码才能执行有用的操作。
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
        }

        /// <summary>
        /// 服务停止:指示在服务停止运行时应发生什么情况。
        /// </summary>
        protected override void OnStop()
        {
        }

        /// <summary>
        /// 暂停:指示在服务暂停时应发生什么情况。
        /// </summary>
        protected override void OnPause()
        {
        }

        /// <summary>
        /// 继续:指示服务在暂停后恢复正常运行时应发生什么情况。
        /// </summary>
        protected override void OnContinue()
        {
        }

        /// <summary>
        /// 停止前:指示在系统关闭之前应发生什么情况(如果此时服务正在运行)。
        /// </summary>
        protected override void OnShutdown()
        {
        }

2.3-Configuration log (log4net)

In order to facilitate testing, let's first introduce how to use log4net for logging. We will record when the log starts and stops.

(1) We create a new folder [LogConfig] in the project directory, and then create a file [log4net.config].

(2) The content of [log4net.config] is as follows.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
	</configSections>

	<system.web>
		<compilation debug="true" targetFramework="4.5.2" />
		<httpRuntime targetFramework="4.5.2" />
	</system.web>
	<log4net>
		<!--错误日志:::记录错误日志-->
		<!--按日期分割日志文件 一天一个-->
		<!-- appender 定义日志输出方式   将日志以回滚文件的形式写到文件中。-->
		<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
			<!--保存路径:下面路径项目启动的时候自动在C盘中创建log、logError文件-->
			<file value="log/error/error_" />
			<!-- 如果想在本项目中添加路径,那就直接去掉C:\\  只设置log\\LogError   项目启动中默认创建文件 -->
			<appendToFile value="true"/>
			<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
			<rollingStyle value="Date"/>
			<!--这是按日期产生文件夹-->
			<datePattern value="yyyy-MM-dd'.log'"/>
			<!--是否只写到一个文件中-->
			<staticLogFileName value="false"/>
			<!--保留的log文件数量 超过此数量后 自动删除之前的   好像只有在 按Size分割时有效 设定值value="-1"为不限文件数-->
			<param name="MaxSizeRollBackups" value="100"/>
			<!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
			<maximumFileSize value="50MB" />
			<!-- layout 控制Appender的输出格式,也可以是xml  一个Appender只能是一个layout-->
			<layout type="log4net.Layout.PatternLayout">
				<!--每条日志末尾的文字说明-->
				<!--输出格式 模板-->
				<!-- <param name="ConversionPattern"  value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger   
        操作者ID:%property{Operator} 操作类型:%property{Action}%n  当前机器名:%property%n当前机器名及登录用户:%username %n  
        记录位置:%location%n 消息描述:%property{Message}%n   异常:%exception%n 消息:%message%newline%n%n" />-->

				<!--样例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
				<!--<conversionPattern value="%newline %n记录时间:%date %n线程ID:[%thread] %n日志级别: %-5level %n错误描述:%message%newline %n"/>-->
				<conversionPattern value="%n==========
                                  %n【日志级别】%-5level
                                  %n【记录时间】%date
                                  %n【执行时间】[%r]毫秒
                                  %n【出错文件】%F
                                  %n【出错行号】%L
                                  %n【出错的类】%logger 属性[%property{NDC}]
                                  %n【错误描述】%message
                                  %n【错误详情】%newline"/>
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter,log4net">
				<levelMin value="ERROR" />
				<levelMax value="FATAL" />
			</filter>
		</appender>

		<!--DEBUG:::记录DEBUG日志-->
		<!--按日期分割日志文件 一天一个-->
		<!-- appender 定义日志输出方式   将日志以回滚文件的形式写到文件中。-->
		<appender name="DebugAppender" type="log4net.Appender.RollingFileAppender">
			<!--保存路径:下面路径项目启动的时候自动在C盘中创建log、logError文件-->
			<file value="log/debug/debug_" />
			<!-- 如果想在本项目中添加路径,那就直接去掉C:\\  只设置log\\LogError   项目启动中默认创建文件 -->
			<appendToFile value="true"/>
			<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
			<rollingStyle value="Date"/>
			<!--这是按日期产生文件夹-->
			<datePattern value="yyyy-MM-dd'.log'"/>
			<!--是否只写到一个文件中-->
			<staticLogFileName value="false"/>
			<!--保留的log文件数量 超过此数量后 自动删除之前的   好像只有在 按Size分割时有效 设定值value="-1"为不限文件数-->
			<param name="MaxSizeRollBackups" value="100"/>
			<!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
			<maximumFileSize value="50MB" />
			<!-- layout 控制Appender的输出格式,也可以是xml  一个Appender只能是一个layout-->
			<layout type="log4net.Layout.PatternLayout">
				<!--每条日志末尾的文字说明-->
				<!--输出格式 模板-->
				<!-- <param name="ConversionPattern"  value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger   
        操作者ID:%property{Operator} 操作类型:%property{Action}%n  当前机器名:%property%n当前机器名及登录用户:%username %n  
        记录位置:%location%n 消息描述:%property{Message}%n   异常:%exception%n 消息:%message%newline%n%n" />-->

				<!--样例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
				<!--<conversionPattern value="%newline %n记录时间:%date %n线程ID:[%thread] %n日志级别: %-5level %n错误描述:%message%newline %n"/>-->
				<conversionPattern value="%n==========
                                  %n【日志级别】%-2level
                                  %n【记录时间】%date
                                  %n【执行时间】[%r]毫秒
                                  %n【debug文件】%F
                                  %n【debug行号】%L
                                  %n【debug类】%logger 属性[%property{NDC}]
                                  %n【debug描述】%message"/>
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter,log4net">
				<levelMin value="DEBUG" />
				<levelMax value="WARN" />
			</filter>
		</appender>


		<!--INFO:::记录INFO日志-->
		<!--按日期分割日志文件 一天一个-->
		<!-- appender 定义日志输出方式   将日志以回滚文件的形式写到文件中。-->
		<appender name="INFOAppender" type="log4net.Appender.RollingFileAppender">
			<!--保存路径:下面路径项目启动的时候自动在C盘中创建log、logError文件-->
			<file value="log/info/info_" />
			<!-- 如果想在本项目中添加路径,那就直接去掉C:\\  只设置log\\LogError   项目启动中默认创建文件 -->
			<appendToFile value="true"/>
			<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
			<rollingStyle value="Date"/>
			<!--这是按日期产生文件夹-->
			<datePattern value="yyyy-MM-dd'.log'"/>
			<!--是否只写到一个文件中-->
			<staticLogFileName value="false"/>
			<!--保留的log文件数量 超过此数量后 自动删除之前的   好像只有在 按Size分割时有效 设定值value="-1"为不限文件数-->
			<param name="MaxSizeRollBackups" value="100"/>
			<!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
			<maximumFileSize value="50MB" />
			<!-- layout 控制Appender的输出格式,也可以是xml  一个Appender只能是一个layout-->
			<layout type="log4net.Layout.PatternLayout">
				<!--每条日志末尾的文字说明-->
				<!--输出格式 模板-->
				<!-- <param name="ConversionPattern"  value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger   
        操作者ID:%property{Operator} 操作类型:%property{Action}%n  当前机器名:%property%n当前机器名及登录用户:%username %n  
        记录位置:%location%n 消息描述:%property{Message}%n   异常:%exception%n 消息:%message%newline%n%n" />-->

				<!--样例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
				<!--<conversionPattern value="%newline %n记录时间:%date %n线程ID:[%thread] %n日志级别: %-5level %n错误描述:%message%newline %n"/>-->
				<conversionPattern value="%n==========
                                  %n【日志级别】%-2level
                                  %n【记录时间】%date
                                  %n【执行时间】[%r]毫秒
                                  %n【info文件】%F
                                  %n【info行号】%L
                                  %n【info类】%logger 属性[%property{NDC}]
                                  %n【info描述】%message"/>
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter,log4net">
				<levelMin value="INFO" />
				<levelMax value="WARN" />
			</filter>
		</appender>

		<!--Set root logger level to DEBUG and its only appender to A1-->
		<root>
			<!--控制级别,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
			<level value="ALL" />
			<appender-ref ref="DebugAppender" />
			<appender-ref ref="ErrorAppender" />
			<appender-ref ref="INFOAppender" />
		</root>
	</log4net>
</configuration>

(3) And right-click the [[log4net.config]] file, click [Properties], and then set [Copy to Output Directory] to [Always Copy].

(4) Then install log4net. Right-click [References] in the project directory, and then click [Manage NuGet Packages]

(5) Then click Browse, search for [log4net], and click Install on the right side.

(6) Important: Then configure the [AssemblyInfo.cs] file. If you do not configure it, the log will not be output.

Just add it to the bottom: (If your [log4net.config] file path is different from mine, remember to change it to the same as your own configuration path).

Code:

[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "config", ConfigFile = "LogConfig/log4net.config", Watch = true)]

 (7) In the service startup method [OnStart], configure to start log4net.

Code:

XmlConfigurator.Configure(new System.IO.FileInfo("LogConfig/log4net.config"));

(8) Then you can use log4net. First obtain an instance of log4net in the Windows service.

Code:

 private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

3. Operation and deployment of Windows services

3.1-Basic configuration of service

(1) Click on our service [MyDemoService.cs], then right-click and click [Add Installer].

(2) Then you can see an extra file below, which is the installation program.

(3) Then you can modify the basic information, [Service Name] [Service Description] and so on in the service component. We right-click [serviceInstall1], click Properties, and then modify it.

(4) Then click [serviceProcessInstall1], right-click the properties, and modify it.

3.2-Service operation and release

When we press F5 or run the project directly in other ways, we will be prompted: "The service cannot be started from the command line or debugger." You must first install the Windows service (using installutil.exe) and then start it using Server Explorer, the Windows Service Management Tool, or the NET START command. ". This is not how it works. Follow the steps below to run and publish Windows services.

Prerequisite note: If the target platform you set is x64, the directory you open will be different, otherwise the service will not run. You can right-click the project name, click [Properties] ->[Generate] ->[Target Platform] to view.

If it is not the x64 version, copy this address:

C:\Windows\Microsoft.NET\Framework\v4.0.30319

If it is the x64 version, copy this address:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319

Otherwise, an error similar to this will be reported:An exception occurred during initial installation: System.BadImageFormatException: Failed to load file or assembly...

(1) Then we add the above address (selected according to your own environment) to the environment variable. Click [Control Panel]——>[System and Security]

(2) Then click [System]

(3) Click [Advanced System Settings]

(4) Click [Environment Variables]

(5) Find Path in [System Variables], and then click [Edit].

(6) Then click [New], and then copy the directory we copied here. Then click Confirm.

(7) To test whether the configuration is successful, enter this command to check [InstallUtil]. If it looks like the following, it means it is successful.

(8) Then edit the solution and project.

(9) Run the cmd command as an administrator and then install the service.

InstallUtil project startup execution file full path

Example of Xigua programmer:

InstallUtil D:\Project demonstration temporary save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

(10) If this appears, the installation is successful.

(11) Open the service manager, find the service you want to start, and then right-click to start the service.

After starting, you can see the log:

(12) If you want to uninstall the service, you can run this command:

InstallUtil /u Full path to project startup executable file

Example of Xigua programmer:

InstallUtil /u D:\Project demonstration temporary save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

3.3-Common commands

1. Installation service: Full path of the InstallUtil project startup execution file

3. Start the service: net start service name

4. Stop the service: net stop service name

4. Uninstall the service: InstallUtil /u The full path of the project startup execution file

3.4-View computer events

If an error occurs when starting a Windows service, we can query the specific error information by viewing computer events.

(1) Press [Win+R] on the keyboard, and then enter [eventvwr.msc]

(2) Then click OK to open the event viewer window. Find [Windows Log] -> [System] on the left side of the window, and you can see the computer's startup, shutdown and log records.

3.5- Install multiple instances of the same Windows service on the server

You can refer to this solution for comparison:Multiple instances_Install multiple instances of the same Windows service on the server_Programmer Toolbox

I am a Xigua programmer and I use this method:

(1) Multiple deployments can be made through this command. Just [servicename] is the only one. Configure it yourself.

sc create [servicename] binpath= [path to your exe]

Note: [servicename] fills in the service name, [path to your exe] is the path to the executable file, it must be the complete path, don’t forget the space after binpath=. This method does allow multiple installations of the service. But the service installer provides all the information. F.e. Description, login type, etc. are ignored.

3.6-Common mistakes

1. Unable to open the Service Control Manager on computer "."

An exception occurred during the "install" phase.

System.InvalidOperationException: Unable to open the Service Control Manager on computer '.' This operation may require additional privileges.

An internal exception System.ComponentModel.Win32Exception was thrown with the following error message: Access Denied. .

Starting the "rollback" phase of the installation.

View the contents of the log file for the progress of the D:\Project Demo Temporary Save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe assembly.

The file is located in D:\Project Demo Temporary Save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.InstallLog.

Rolling back assembly "D:\Project Demo Temporary Save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe".

The affected parameters are:

logtoconsole =

logfile = D:\Project demonstration temporary save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.InstallLog

assemblypath = D:\Project demonstration temporary save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

Restore event log to previous state of source TestDemoServices.

The "rollback" phase has been completed successfully.

Transaction installation is complete.

Installation failed, rollback has been performed.

Solution: Insufficient permissions. Remember to open cmd [as administrator] when opening cmd.

2. Unable to load basic dependencies of file or assembly xxx

Exception occurred during initial installation: System.BadImageFormatException: Unable to load a dependency of file or assembly 'file://E:\DebuginServers.sB'. An attempt was made to load a program that is not in the correct format.

Solution: Please see point [3.2] in this article

3. When executing the [.bat] file, there is no relevant error in the reported path.

Execute command: InstallUtil /u D:\Project demonstration temporary save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

Error message: C:\Windows\system32>InstallUtil /u D:\椤gui洰婕码ず涓综合椂奇濆瓨\MyDemoService\MyDemoService\bin\Debug \MyDemoService.exe Microsoft (R) .NET Framework Installation Utility Version 4.8.9037.0 Copyright (C) Microsoft Corporation. all rights reserved. Exception occurred during initial installation: System.IO.FileNotFoundException: Unable to load file or assembly 'file:///D:\椤Gui洰ejieqiず涓椂Q濆瓨\MyDemoService\MyDemoService\bin\Debug\ MyDemoService.exe" or one of its dependencies. The system can not find the file specified. .

Analysis: This error is caused by garbled characters. The executed command is this [InstallUtil /u D:\Project Demonstration Temporary Save\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe], but what is displayed in the console is this [file :///D:\椤gui氰婕码ず涓综合椂Q濆瓨\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe]. Garbled characters appear, which may be caused by incorrect console encoding settings.

Resolution: To use the correct encoding, try setting the console encoding to match the encoding used by the file path. For example, if the file path is UTF-8 encoded, you can execute the following command in the console to set it:

chcp 65001

screenshot:

4. Efficient tool: write bat to start the uninstall service

4.1-Installation Services

We can create a file [Installation Service.bat] and enter the following content:

chcp 65001
REM Install
InstallUtil 项目启动执行文件全路径
pause

Note: To execute this command, you need to run it [as an administrator], otherwise this error will be reported:

System.InvalidOperationException: Unable to open the Service Control Manager on computer '.' This operation may require additional privileges.

An internal exception System.ComponentModel.Win32Exception was thrown with the following error message: Access Denied. .

Correct way to do it:

4.2-Start the service

We can create a file [Uninstall Service.bat] and enter the following content:

REM Install
net start 服务名
pause

Note: To execute this command, you need to run it [as an administrator], otherwise this error will be reported:

A system error has occurred.

access denied.

4.3-Stop service

We can create a file [Uninstall Service.bat] and enter the following content:

REM Install
net stop 服务名
pause

Note: To execute this command, you need to run it [as an administrator], otherwise this error will be reported:

A system error has occurred.

access denied.

4.4-Uninstall the service

We can create a file [Uninstall Service.bat] and enter the following content:

chcp 65001
REM Install
InstallUtil /u  项目启动执行文件全路径
pause

Copyright statement: This article is an original article, and the copyright belongs to [Xigua Programmer]. Please indicate the source when reprinting. If you have any questions, please send a private message.

Original link:Using C# to create and install a Windows service program (the most complete tutorial)_Xigua Programmer’s Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/2301_79251107/article/details/132339427