diff --git a/aspnetcore/host-and-deploy/windows-service.md b/aspnetcore/host-and-deploy/windows-service.md index 3bc0250c4df5..44aadc6d2cce 100644 --- a/aspnetcore/host-and-deploy/windows-service.md +++ b/aspnetcore/host-and-deploy/windows-service.md @@ -1,11 +1,11 @@ --- title: Host in a Windows Service author: tdykstra -description: Learn how to host an ASP.NET Core application in a Windows Service. +description: Learn how to host an ASP.NET Core app in a Windows Service. manager: wpickett ms.author: tdykstra ms.custom: mvc -ms.date: 03/30/2017 +ms.date: 01/30/2018 ms.prod: aspnet-core ms.technology: aspnet ms.topic: article @@ -15,106 +15,106 @@ uid: host-and-deploy/windows-service By [Tom Dykstra](https://github.com/tdykstra) -The recommended way to host an ASP.NET Core app on Windows without using IIS is to run it in a [Windows Service](https://docs.microsoft.com/dotnet/framework/windows-services/introduction-to-windows-service-applications). That way it can automatically start after reboots and crashes, without waiting for someone to log in. +The recommended way to host an ASP.NET Core app on Windows without using IIS is to run it in a [Windows Service](/dotnet/framework/windows-services/introduction-to-windows-service-applications). When hosted as a Windows Service, the app can automatically start after reboots and crashes without requiring human intervention. -[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/host-and-deploy/windows-service/sample) ([how to download](xref:tutorials/index#how-to-download-a-sample)). See the [Next Steps](#next-steps) section for instructions on how to run it. +[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/host-and-deploy/windows-service/sample) ([how to download](xref:tutorials/index#how-to-download-a-sample)). For instructions on how to run the sample app, see the sample's *README.md* file. ## Prerequisites -* The app must run on the .NET Framework runtime. In the *.csproj* file, specify appropriate values for [TargetFramework](https://docs.microsoft.com/nuget/schema/target-frameworks) and [RuntimeIdentifier](https://docs.microsoft.com/dotnet/articles/core/rid-catalog). Here's an example: +* The app must run on the .NET Framework runtime. In the *.csproj* file, specify appropriate values for [TargetFramework](/nuget/schema/target-frameworks) and [RuntimeIdentifier](/dotnet/articles/core/rid-catalog). Here's an example: [!code-xml[](windows-service/sample/AspNetCoreService.csproj?range=3-6)] When creating a project in Visual Studio, use the **ASP.NET Core Application (.NET Framework)** template. -* If the app receives requests from the Internet (not just from an internal network), it must use the [WebListener](xref:fundamentals/servers/weblistener) web server rather than [Kestrel](xref:fundamentals/servers/kestrel). Kestrel must be used with IIS for edge deployments. For more information, see [When to use Kestrel with a reverse proxy](xref:fundamentals/servers/kestrel#when-to-use-kestrel-with-a-reverse-proxy). +* If the app receives requests from the Internet (not just from an internal network), it must use the [HTTP.sys](xref:fundamentals/servers/httpsys) web server (formerly known as [WebListener](xref:fundamentals/servers/weblistener) for ASP.NET Core 1.x apps) rather than [Kestrel](xref:fundamentals/servers/kestrel). IIS is recommended for use as a reverse proxy server with Kestrel for edge deployments. For more information, see [When to use Kestrel with a reverse proxy](xref:fundamentals/servers/kestrel#when-to-use-kestrel-with-a-reverse-proxy). ## Getting started This section explains the minimum changes required to set up an existing ASP.NET Core project to run in a service. -* Install the NuGet package [Microsoft.AspNetCore.Hosting.WindowsServices](https://www.nuget.org/packages/Microsoft.AspNetCore.Hosting.WindowsServices/). +1. Install the NuGet package [Microsoft.AspNetCore.Hosting.WindowsServices](https://www.nuget.org/packages/Microsoft.AspNetCore.Hosting.WindowsServices/). -* Make the following changes in `Program.Main`: +1. Make the following changes in `Program.Main`: - * Call `host.RunAsService` instead of `host.Run`. + * Call `host.RunAsService` instead of `host.Run`. - * If the code calls `UseContentRoot`, use a path to the publish location instead of `Directory.GetCurrentDirectory()` - - [!code-csharp[](windows-service/sample/Program.cs?name=ServiceOnly&highlight=3-4,8,14)] + * If the code calls `UseContentRoot`, use a path to the publish location instead of `Directory.GetCurrentDirectory()`. -* Publish the application to a folder. + # [ASP.NET Core 2.x](#tab/aspnetcore2x) - Use [dotnet publish](https://docs.microsoft.com/dotnet/articles/core/tools/dotnet-publish) or a [Visual Studio publish profile](xref:host-and-deploy/visual-studio-publish-profiles) that publishes to a folder. + [!code-csharp[](windows-service/sample/Program.cs?name=ServiceOnly&highlight=3-4,7,12)] -* Test by creating and starting the service. + # [ASP.NET Core 1.x](#tab/aspnetcore1x) - Open an administrator command prompt window to use the [sc.exe](https://technet.microsoft.com/library/bb490995) command-line tool to create and start a service. - - If the service is named MyService, publish the app to `c:\svc`, and the app itself is named AspNetCoreService, the commands would look like this: + [!code-csharp[](windows-service/sample_snapshot/Program.cs?name=ServiceOnly&highlight=3-4,8,14)] + + --- + +1. Publish the app to a folder. Use [dotnet publish](/dotnet/articles/core/tools/dotnet-publish) or a [Visual Studio publish profile](xref:host-and-deploy/visual-studio-publish-profiles) that publishes to a folder. - ```console - sc create MyService binPath="C:\Svc\AspNetCoreService.exe" - sc start MyService - ``` +1. Test by creating and starting the service. - The `binPath` value is the path to the app's executable, including the executable filename itself. + Open a command shell with administrative privileges to use the [sc.exe](https://technet.microsoft.com/library/bb490995) command-line tool to create and start a service. If the service is named MyService, published to `c:\svc`, and named AspNetCoreService, the commands are: - ![Console window create and start example](windows-service/_static/create-start.png) + ```console + sc create MyService binPath="c:\svc\aspnetcoreservice.exe" + sc start MyService + ``` - When these commands finish, browse to the same path as when running as a console app (by default, `http://localhost:5000`) + The `binPath` value is the path to the app's executable, which includes the executable file name. - ![Running in a service](windows-service/_static/running-in-service.png) + ![Console window create and start example](windows-service/_static/create-start.png) + When these commands finish, browse to the same path as when running as a console app (by default, `http://localhost:5000`): + + ![Running in a service](windows-service/_static/running-in-service.png) ## Provide a way to run outside of a service -It's easier to test and debug when running outside of a service, so it's customary to add code that calls `host.RunAsService` only under certain conditions. For example, the app can run as a console app with a `--console` command-line argument or if the debugger is attached. +It's easier to test and debug when running outside of a service, so it's customary to add code that calls `RunAsService` only under certain conditions. For example, the app can run as a console app with a `--console` command-line argument or if the debugger is attached: -[!code-csharp[](windows-service/sample/Program.cs?name=ServiceOrConsole)] +# [ASP.NET Core 2.x](#tab/aspnetcore2x) -## Handle stopping and starting events +[!code-csharp[](windows-service/sample/Program.cs?name=ServiceOrConsole)] -To handle `OnStarting`, `OnStarted`, and `OnStopping` events, make the following additional changes: +# [ASP.NET Core 1.x](#tab/aspnetcore1x) -* Create a class that derives from `WebHostService`. +[!code-csharp[](windows-service/sample_snapshot/Program.cs?name=ServiceOrConsole)] - [!code-csharp[](windows-service/sample/CustomWebHostService.cs?name=NoLogging)] +--- -* Create an extension method for `IWebHost` that passes the custom `WebHostService` to `ServiceBase.Run`. +## Handle stopping and starting events - [!code-csharp[](windows-service/sample/WebHostServiceExtensions.cs?name=ExtensionsClass)] +To handle `OnStarting`, `OnStarted`, and `OnStopping` events, make the following additional changes: -* In `Program.Main` change call the new extension method instead of `host.RunAsService`. +1. Create a class that derives from `WebHostService`: - [!code-csharp[](windows-service/sample/Program.cs?name=HandleStopStart&highlight=26)] + [!code-csharp[](windows-service/sample/CustomWebHostService.cs?name=NoLogging)] -If the custom `WebHostService` code needs to get a service from dependency injection (such as a logger), get it from the `Services` property of `IWebHost`. +1. Create an extension method for `IWebHost` that passes the custom `WebHostService` to `ServiceBase.Run`: -[!code-csharp[](windows-service/sample/CustomWebHostService.cs?name=Logging&highlight=7)] + [!code-csharp[](windows-service/sample/WebHostServiceExtensions.cs?name=ExtensionsClass)] -## Next steps +1. In `Program.Main`, call the new extension method, `RunAsCustomService`, instead of `RunAsService`: -The [sample application](https://github.com/aspnet/Docs/tree/master/aspnetcore/host-and-deploy/windows-service/sample) that accompanies this article is a simple MVC web app that has been modified as shown in preceding code examples. To run it in a service, do the following steps: + # [ASP.NET Core 2.x](#tab/aspnetcore2x) -* Publish to *c:\svc*. + [!code-csharp[](windows-service/sample/Program.cs?name=HandleStopStart&highlight=24)] -* Open an administrator window. + # [ASP.NET Core 1.x](#tab/aspnetcore1x) -* Enter the following commands: + [!code-csharp[](windows-service/sample_snapshot/Program.cs?name=HandleStopStart&highlight=26)] - ```console - sc create MyService binPath="c:\svc\aspnetcoreservice.exe" - sc start MyService - ``` + --- - * In a browser, go to http://localhost:5000 to verify that it's running. +If the custom `WebHostService` code requires a service from dependency injection (such as a logger), obtain it from the `Services` property of `IWebHost`: -If the app doesn't start up as expected when running in a service, a quick way to make error messages accessible is to add a logging provider such as the [Windows EventLog provider](xref:fundamentals/logging/index#eventlog). +[!code-csharp[](windows-service/sample/CustomWebHostService.cs?name=Logging&highlight=7)] ## Acknowledgments -This article was written with the help of sources that were already published. The earliest and most useful of them were these: +This article was written with the help of published sources: * [Hosting ASP.NET Core as Windows service](https://stackoverflow.com/questions/37346383/hosting-asp-net-core-as-windows-service/37464074) * [How to host your ASP.NET Core in a Windows Service](https://dotnetthoughts.net/how-to-host-your-aspnet-core-in-a-windows-service/) diff --git a/aspnetcore/host-and-deploy/windows-service/sample/AspNetCoreService.csproj b/aspnetcore/host-and-deploy/windows-service/sample/AspNetCoreService.csproj index 1a49744963b4..1c16926d0113 100644 --- a/aspnetcore/host-and-deploy/windows-service/sample/AspNetCoreService.csproj +++ b/aspnetcore/host-and-deploy/windows-service/sample/AspNetCoreService.csproj @@ -1,21 +1,24 @@  - net452 - win7-x86 + net461 + win7-x64 + - - - - - - - + + + + + + + + - + + Component diff --git a/aspnetcore/host-and-deploy/windows-service/sample/Program.cs b/aspnetcore/host-and-deploy/windows-service/sample/Program.cs index 0c9b26872a68..1bc533dd7769 100644 --- a/aspnetcore/host-and-deploy/windows-service/sample/Program.cs +++ b/aspnetcore/host-and-deploy/windows-service/sample/Program.cs @@ -1,9 +1,9 @@ #define HandleStopStart // or ServiceOnly ServiceOrConsole -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.WindowsServices; using System.Diagnostics; using System.IO; using System.Linq; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; namespace AspNetCoreService { @@ -16,13 +16,11 @@ public static void Main(string[] args) var pathToExe = Process.GetCurrentProcess().MainModule.FileName; var pathToContentRoot = Path.GetDirectoryName(pathToExe); - var host = new WebHostBuilder() - .UseKestrel() - .UseContentRoot(pathToContentRoot) - .UseIISIntegration() - .UseStartup() - .UseApplicationInsights() - .Build(); + var host = WebHost.CreateDefaultBuilder(args) + .UseContentRoot(pathToContentRoot) + .UseStartup() + .UseApplicationInsights() + .Build(); host.RunAsService(); } @@ -45,13 +43,11 @@ public static void Main(string[] args) pathToContentRoot = Path.GetDirectoryName(pathToExe); } - var host = new WebHostBuilder() - .UseKestrel() - .UseContentRoot(pathToContentRoot) - .UseIISIntegration() - .UseStartup() - .UseApplicationInsights() - .Build(); + var host = WebHost.CreateDefaultBuilder(args) + .UseContentRoot(pathToContentRoot) + .UseStartup() + .UseApplicationInsights() + .Build(); if (isService) { @@ -81,13 +77,11 @@ public static void Main(string[] args) pathToContentRoot = Path.GetDirectoryName(pathToExe); } - var host = new WebHostBuilder() - .UseKestrel() - .UseContentRoot(pathToContentRoot) - .UseIISIntegration() - .UseStartup() - .UseApplicationInsights() - .Build(); + var host = WebHost.CreateDefaultBuilder(args) + .UseContentRoot(pathToContentRoot) + .UseStartup() + .UseApplicationInsights() + .Build(); if (isService) { diff --git a/aspnetcore/host-and-deploy/windows-service/sample/README.md b/aspnetcore/host-and-deploy/windows-service/sample/README.md new file mode 100644 index 000000000000..7956175aeff1 --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/README.md @@ -0,0 +1,43 @@ +# Custom WebHost Service Sample + +This sample shows the recommended way to host an ASP.NET Core app on Windows without using IIS as a Windows Service. This sample demonstrates the features described in [Host an ASP.NET Core app in a Windows Service](https://docs.microsoft.com/aspnet/core/host-and-deploy/windows-service). + +## Instructions + +The sample app is a simple MVC web app modified according to the instructions in [Host an ASP.NET Core app in a Windows Service](https://docs.microsoft.com/aspnet/core/host-and-deploy/windows-service). + +To run the app in a service, perform the following steps: + +1. Create a folder at *c:\svc*. + +1. Publish the app to the folder with `dotnet publish --configuration Release --output c:\\svc`. The command will move the app's assets to the folder, including the required `appsettings.json` file and the `wwwroot` folder with its contents. + +1. Open an **administrator** command shell. + +1. Execute the following commands: + + ```console + sc create MyService binPath="c:\svc\aspnetcoreservice.exe" + sc start MyService + ``` + +1. In a browser, go to `http://localhost:5000` to verify that the service is running. + +1. To stop the service, use the command: + + ```console + sc stop MyService + ``` + +If the app doesn't start up as expected when running in a service, a quick way to make error messages accessible is to add a logging provider, such as the [Windows EventLog provider](https://docs.microsoft.com/aspnet/core/fundamentals/logging/index#eventlog). Another option is to check the Application Event Log using the Event Viewer on the system. For example, here's an unhandled exception for a FileNotFound error in the Application Event Log: + +```console +Application: AspNetCoreService.exe +Framework Version: v4.0.30319 +Description: The process was terminated due to an unhandled exception. +Exception Info: System.IO.FileNotFoundException + at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean) + at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(System.Collections.Generic.IList`1) + at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build() + ... +``` diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/css/site.css b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/css/site.css new file mode 100644 index 000000000000..c08311ba3868 --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/css/site.css @@ -0,0 +1,21 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +h1 { + font-size: 30px; +} + +h2 { + font-size: 24px; +} + +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +.panel-body { + font-size: 16px; +} diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/css/site.min.css b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/css/site.min.css new file mode 100644 index 000000000000..69c38efcc68d --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/css/site.min.css @@ -0,0 +1 @@ +body{padding-top:50px;padding-bottom:20px}h1{font-size:30px}h2{font-size:24px}.body-content{padding-left:15px;padding-right:15px}.panel-body{font-size:16px} diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/favicon.ico b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/favicon.ico new file mode 100644 index 000000000000..a3a799985c43 Binary files /dev/null and b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/favicon.ico differ diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner1.svg b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner1.svg new file mode 100644 index 000000000000..1ab32b60b899 --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner2.svg b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner2.svg new file mode 100644 index 000000000000..9679c604d00d --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner3.svg b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner3.svg new file mode 100644 index 000000000000..9be2c2503c1a --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner3.svg @@ -0,0 +1 @@ +banner3b \ No newline at end of file diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner4.svg b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner4.svg new file mode 100644 index 000000000000..38b3d7cd1f2a --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/images/banner4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/js/site.js b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/js/site.js new file mode 100644 index 000000000000..82ecce7b4a78 --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/js/site.js @@ -0,0 +1 @@ +// Write your Javascript code. diff --git a/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/js/site.min.js b/aspnetcore/host-and-deploy/windows-service/sample/wwwroot/js/site.min.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/aspnetcore/host-and-deploy/windows-service/sample_snapshot/Program.cs b/aspnetcore/host-and-deploy/windows-service/sample_snapshot/Program.cs new file mode 100644 index 000000000000..8f9cfa64e89b --- /dev/null +++ b/aspnetcore/host-and-deploy/windows-service/sample_snapshot/Program.cs @@ -0,0 +1,104 @@ +#define HandleStopStart // or ServiceOnly ServiceOrConsole +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.WindowsServices; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace AspNetCoreService +{ + public class Program + { +#if ServiceOnly + #region ServiceOnly + public static void Main(string[] args) + { + var pathToExe = Process.GetCurrentProcess().MainModule.FileName; + var pathToContentRoot = Path.GetDirectoryName(pathToExe); + + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(pathToContentRoot) + .UseIISIntegration() + .UseStartup() + .UseApplicationInsights() + .Build(); + + host.RunAsService(); + } + #endregion +#endif +#if ServiceOrConsole +#region ServiceOrConsole + public static void Main(string[] args) + { + bool isService = true; + if (Debugger.IsAttached || args.Contains("--console")) + { + isService = false; + } + + var pathToContentRoot = Directory.GetCurrentDirectory(); + if (isService) + { + var pathToExe = Process.GetCurrentProcess().MainModule.FileName; + pathToContentRoot = Path.GetDirectoryName(pathToExe); + } + + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(pathToContentRoot) + .UseIISIntegration() + .UseStartup() + .UseApplicationInsights() + .Build(); + + if (isService) + { + host.RunAsService(); + } + else + { + host.Run(); + } + } +#endregion +#endif +#if HandleStopStart +#region HandleStopStart + public static void Main(string[] args) + { + bool isService = true; + if (Debugger.IsAttached || args.Contains("--console")) + { + isService = false; + } + + var pathToContentRoot = Directory.GetCurrentDirectory(); + if (isService) + { + var pathToExe = Process.GetCurrentProcess().MainModule.FileName; + pathToContentRoot = Path.GetDirectoryName(pathToExe); + } + + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(pathToContentRoot) + .UseIISIntegration() + .UseStartup() + .UseApplicationInsights() + .Build(); + + if (isService) + { + host.RunAsCustomService(); + } + else + { + host.Run(); + } + } +#endregion +#endif + } +}