我看到了一些奇怪的解决方案,将所有代码放入.Net Standard类库中,然后使用.Net Framework应用程序来托管Windows服务,但这在我看来并不优雅,而且我正在尝试完全摆脱.Net Framework。
我想做的事情现在是否可能?我是否忽略了一些非常基本的东西?
特别是,在.NET Core中编写Windows服务现在可能是可行的,但您不会在开箱即用时获得跨平台兼容性,因为除Windows外的其他平台的程序集只会在尝试使用服务代码时抛出但在开始移植之前,您应该了解您希望通过迁移实现什么。仅仅因为它是一个新的.NET实现而将其移植到.NET Core并不是一个足够好的理由(除非你是一个真正的粉丝)。
PlatformNotSupportedException
。虽然可以通过一些方法解决此问题(例如使用RuntimeInformation.IsOSPlatform
),但这是另一个问题。2.0.0-preview1-26216-02
)不支持System.Configuration.Install
命名空间,因此默认方法使用ServiceProcessInstaller
类和installutil
无法工作。稍后会有更多详细信息。Service1
),那么只需要编辑并替换.csproj
为新格式即可使其在.NET Core 2.0上构建。<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp20</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="2.0.0-*" />
</ItemGroup>
</Project>
然后删除properties\AssemblyInfo.cs
,因为它不再需要,并且会与项目本身的版本信息冲突。
如果您已经有一个服务并且它有依赖关系,则转换可能更加复杂。请参见此处。
现在,您应该能够运行dotnet publish
并获得一个可执行文件。如上所述,您不能使用ServiceProcessInstaller
类来安装服务,因此您必须手动执行以下操作:
这可以通过一些 PowerShell 来完成。从包含您发布的可执行文件的位置中的提升的提示符中:
$messageResourceFile = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\EventLogMessages.dll"
New-EventLog -LogName Application -Source Service1 -MessageResourceFile $messageResourceFile
sc.exe create Service1 binPath= (Resolve-Path .\WindowsService1.exe)
Program.cs
中进行一些命令行解析或使用Cocowalla's answer中提到的库之一来为项目提供自己的安装功能。如何将.NET Core 2.0 Web API托管为Windows服务。我按照这篇指南在Windows服务中托管ASP.NET Core进行操作。其中的先决条件部分对我来说不太清楚。经过一些错误,以下是我的做法: 源代码
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<!--<TargetFramework>netcoreapp2.0</TargetFramework>-->
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<!--<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />-->
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.3" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.0.2" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
</ItemGroup>
</Project>
我将总结一些选项:
ServiceBase
。当然,这需要在目标机器上安装.NET Framework我认为@JeroenMostert的评论有点苛刻-不依赖于特定的.NET Framework版本在目标机器上可用的吸引力显而易见。正如我链接的这两个仓库所示,许多其他人显然也有相同的感觉。
Install-Package PeterKottas.DotNetCore.WindowsService
这里也有如何入门的好笔记。
我们只需要安装System.ServiceProcess.ServiceController NuGet包,就能将.NET Core应用程序作为Windows服务运行。
下面是.csproj文件的内容:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ServiceProcess.ServiceController"
Version="4.5.0" />
</ItemGroup>
</Project>
Program.cs 文件,
using System.ServiceProcess;
namespace WindowsService101
{
class Program
{
static void Main(string[] args)
{
using (var service = new HelloWorldService())
{
ServiceBase.Run(service);
}
}
}
}
public class HelloWorldService : ServiceBase
{
protected override void OnStart(string[] args)
{
// Code Here
}
protected override void OnStop()
{
// Code Here
}
}
构建并发布解决方案。
从.exe文件夹中以管理员身份打开Cmd提示符 示例:\WindowsService101\bin\Debug\netcoreapp2.1\publish
sc create binPath=""
sc start
.NET Core 2.2
下的Windows服务中的ASP.NET Core。对现有的ASP.NET Core项目进行以下更改,以将应用程序作为服务运行:
要求:PowerShell 6.2或更高版本
基于框架的部署(FDD):
基于框架的部署(FDD)依赖于目标系统上共享的.NET Core的系统范围版本。当在ASP.NET Core Windows服务应用程序中使用FDD场景时,SDK会生成一个名为框架相关可执行文件(* .exe)。
在包含目标框架的<PropertyGroup>
中添加Windows运行时标识符(RID)。在下面的示例中,RID设置为win7-x64
。添加<SelfContained>
属性设置为false
。这些属性指示SDK为Windows生成可执行文件(.exe)。
对于Windows服务应用程序,通常在发布ASP.NET Core应用程序时生成一个web.config文件是不必要的。为禁用web.config文件的创建,请添加<IsTransformWebConfigDisabled>
属性设置为true
。
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
自包含部署(SCD):
自包含部署(SCD)不依赖于目标系统上的共享组件。运行时和应用程序的依赖项与应用一起部署到托管系统上。
确认存在Windows Runtime Identifier (RID),或将 RID 添加到包含目标框架的<PropertyGroup>
中。通过添加<IsTransformWebConfigDisabled>
属性设置为true
来禁用创建web.config文件。
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
Program.Main
public class Program
{
public static void Main(string[] args)
{
var isService = !(Debugger.IsAttached || args.Contains("--console"));
if (isService)
{
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
var builder = CreateWebHostBuilder(
args.Where(arg => arg != "--console").ToArray());
var host = builder.Build();
if (isService)
{
// To run the app without the CustomWebHostService change the
// next line to host.RunAsService();
host.RunAsCustomService();
}
else
{
host.Run();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddEventLog();
})
.ConfigureAppConfiguration((context, config) =>
{
// Configure the app here.
})
.UseStartup<Startup>();
}
dotnet publish --configuration Release --output c:\svc
发布自包含部署(SCD)
在项目文件的 <RuntimeIdentifier>
(或 <RuntimeIdentifiers>
)属性中必须指定RID。将运行时提供给 dotnet publish
命令的 -r|--runtime 选项。
dotnet publish --configuration Release --runtime win7-x64 --output c:\svc
使用管理员权限的 PowerShell 6 命令行通过 icacls 命令授予应用程序文件夹的写入/读取/执行访问权限。
icacls "{PATH}" /grant "{USER ACCOUNT}:(OI)(CI){PERMISSION FLAGS}" /t
命令:
icacls "c:\svc" /grant "ServiceUser:(OI)(CI)WRX" /t
.\RegisterService.ps1
-Name MyService
-DisplayName "My Cool Service"
-Description "This is the Sample App service."
-Exe "c:\svc\SampleApp.exe"
-User Desktop-PC\ServiceUser
使用PowerShell 6命令Start-Service -Name {NAME}
启动服务。
Start-Service -Name MyService
处理启动和停止事件
internal class CustomWebHostService : WebHostService
{
private ILogger _logger;
public CustomWebHostService(IWebHost host) : base(host)
{
_logger = host.Services
.GetRequiredService<ILogger<CustomWebHostService>>();
}
protected override void OnStarting(string[] args)
{
_logger.LogInformation("OnStarting method called.");
base.OnStarting(args);
}
protected override void OnStarted()
{
_logger.LogInformation("OnStarted method called.");
base.OnStarted();
}
protected override void OnStopping()
{
_logger.LogInformation("OnStopping method called.");
base.OnStopping();
}
}
扩展方法:
public static class WebHostServiceExtensions
{
public static void RunAsCustomService(this IWebHost host)
{
var webHostService = new CustomWebHostService(host);
ServiceBase.Run(webHostService);
}
}
Program.Main:
host.RunAsCustomService();
将内容根目录路径设置为应用程序文件夹:
Program.Main:
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
CreateWebHostBuilder(args)
.Build()
.RunAsService();
Source:
https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/host-and-deploy/windows-service/
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-2.2
对于那些想要使用.NET Core 3.x实现Windows服务的人
https://csharp.christiannagel.com/2019/10/15/windowsservice/
通过将通用主机与后台服务以及命令行工具sc结合起来,您就可以拥有一个Windows服务。随着微软发布了Microsoft.Windows.Compatibility,我会使用它,因为它似乎是未来最好的选择。
自安装服务的简单示例在这里https://github.com/janantos/service_core
sc.exe
的几个调用,没有任何解释。那个Github仓库随时可能消失,这将使得此答案无法使用。目前为止,某人必须已经了解服务和sc.exe
,才能理解该仓库包含的内容。 - Panagiotis Kanavos