3

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?

Velocedge
  • 1,222
  • 1
  • 11
  • 35
  • 1
    ASP.NET Core applications, including gRPC servers, are supposed to be hosted within Internet Information Server (IIS). It will provide all the Windows service infrastructure that you need, and it ships with Windows. Why not use IIS? – Seva Alekseyev Aug 11 '22 at 15:35
  • From Microsoft: "Although ASP.NET Core 6.0 applications can be hosted in IIS on Windows Server, currently it isn't possible to host a gRPC application in IIS because some of the HTTP/2 functionality isn't supported" – Velocedge Aug 11 '22 at 16:58
  • How old is that line? – Seva Alekseyev Aug 11 '22 at 17:30
  • Don't know but I've seen it in multiple places and I've been looking everywhere. Haven't seen anyone suggest make it a part of IIS. But I don't really want it to be part of IIS, so can you answer the question? – Velocedge Aug 11 '22 at 19:40
  • 1
    Try to check the `Event Viewer` to find application logs. When configuring windows service host, it automatically adds EventLogs provider. You might want to configure Debug log level too. Additionally, if you want to get a normal `Main` method, check the `Do not use top-level statements` when creating the project. – Artur Aug 15 '22 at 06:33
  • Actually, I had got it to run as a service prior to your comment. I found "Do not use top level statements" required .Net 7.0 so I tried it with and without checking that box... no change. The service runs but it's not listening on the specified port. I edited the text to reflect that. – Velocedge Aug 16 '22 at 14:18
  • By default, the service will start on port 5000. If you want to specify another port add `--urls "https://localhost:6276"` to windows service startup arguments. Check this [article](https://andrewlock.net/5-ways-to-set-the-urls-for-an-aspnetcore-app/) for more info. – Artur Aug 17 '22 at 14:39
  • Did you follow the guidelines provided by Microsoft? https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/self-hosted you might need to publish your app in order to make it run as a Windows service. – madmax Aug 18 '22 at 07:07
  • Autur: That link you posted is exactly why I'm having problems. Look at my program.cs code in the first box. It is nothing like the example in the link. – Velocedge Aug 18 '22 at 15:14
  • Another One: I did look at those guidelines and published the service but no help. – Velocedge Aug 19 '22 at 10:24

1 Answers1

8

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\""
Artur
  • 4,595
  • 25
  • 38
  • This is the ONLY thing I've seen that makes this work. My app worked with the code you see and I'm guessing it's the AddGrpcReflection that did it... although I have no idea what it did. I put the port change in the code as well. I'll edit and show that. – Velocedge Aug 21 '22 at 18:16
  • The reflection service just exposes proto contracts to the outside, so tools like grpcui or postman can generate ui and trigger grpc methods. Not more, not less. – Artur Aug 21 '22 at 18:22