Velocedge
Velocedge

Reputation: 1455

How create Windows Service from VS 2022 created gRPC server?

I've created a gRPC server in Visual Studio 2022 Community Preview by selecting the "ASP NET Core gRPC Service" template and .Net 6 Core. I intend to replace four existing .Net Framework Windows services who are all using WCF. So, I'm not looking for an alternative on how to create a Windows service.

The code generated from VS 2022 creates a program.cs (sans comments) that looks like:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<GreeterService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
app.Run();

Every example I can find does not match this program.cs's contents. Additionally, all the examples include a generated startup.cs file. My project did not create a startup.cs file. All the examples show including the NuGet package Microsoft.Extensions.Hosting.WindowsServices and adding a UseWindowsServices parameter.

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    ...

I don't have a Host or a CreateDefaultBuilder method. I tried adding the line:

builder.Host.UseWindowsService();

The program compiles and works perfectly when running in VS or the command line. I can see the ports with netstat:

netstat -an | find "6276"
C:\Users\Steve>netstat -an | find "6276"
  TCP    127.0.0.1:6276         0.0.0.0:0              LISTENING
  TCP    [::1]:6276             [::]:0                 LISTENING

But when I run it as a Windows Service, it is not listening on the identified port.

netstat -an | find "6276"
C:\Users\Steve>

I tried .Net 6.0 and .Net 7.0 preview 7, checking and unchecking "Do not use top level statements" on the later. No change in the behavior.

So, apparently Visual Studio changed the template output for gRPC and nobody has created a Windows Service with it yet... or at least has not shown how it was done.

Does anyone know how to take the latest gRPC template and create a Windows Service from it?

Upvotes: 5

Views: 6256

Answers (1)

Artur
Artur

Reputation: 5522

Here is an example of the minimal application created by the default template with a few modifications (see code comments)

The project file

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.40.0" />
    <!-- Install additional packages -->
    <PackageReference Include="Grpc.AspNetCore.Server.Reflection" Version="2.40.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
  </ItemGroup>

</Project>

The Program.cs

using GrpcService1.Services;
using Microsoft.Extensions.Hosting.WindowsServices;

// Use WebApplicationOptions to set the ContentRootPath
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    ContentRootPath = WindowsServiceHelpers.IsWindowsService()
        ? AppContext.BaseDirectory
        : default
});

// Set WindowsServiceLifetime
builder.Host.UseWindowsService();

builder.Services.AddGrpc();

// Add reflection services
builder.Services.AddGrpcReflection();

var app = builder.Build();

app.MapGrpcService<GreeterService>();

// Map reflection endpoint
app.MapGrpcReflectionService();

app.Run();

Now open cmd in the project folder and execute

dotnet publish

The publish command will produce the exe file (I assume you are working on a Windows machine, otherwise how would you test Windows Service? 😁). On my machine the path to exe is

"C:\github\junk\GrpcService1\bin\Debug\net6.0\publish\GrpcService1.exe"

Now open cmd as Administrator and run the following command

sc.exe create "GrpcService" binpath="C:\github\junk\GrpcService1\bin\Debug\net6.0\publish\GrpcService1.exe"

Open Services and start the GrpcService. Now install and run grpcui tool:

grpcui -plaintext localhost:5000

The grpcui tool will open the UI where you should be able to see the Greeter serviceenter image description here

Notes:

  • I Used WebApplication.CreateBuilder(new WebApplicationOptions()) because without that the service won't start. The Windows Event Viewer shows the error:

Description: The process was terminated due to an unhandled exception. Exception Info: System.NotSupportedException: The content root changed from "C:\Windows\system32" to "C:\github\junk\GrpcService1\bin\Debug\net6.0\publish". Changing the host configuration using WebApplicationBuilder.Host is not supported. Use WebApplication.CreateBuilder(WebApplicationOptions) instead.

Found the solution here.

  • By default, the application will start on port 5000. If you wish to use another port add --urls argument when creating the service
sc.exe create "GrpcService" binpath="C:\github\junk\GrpcService1\bin\Debug\net6.0\publish\GrpcService1.exe --urls \"http://localhost:6276\""

Upvotes: 11

Related Questions