dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.65k stars 1.06k forks source link

The .NET 5 return Platform Not Supported if you try to use .NET 5 as a Windows Service application. #16049

Closed MycroBeat closed 3 years ago

MycroBeat commented 3 years ago

Hello. Recently, i wanted to upgrade my websites from .net core 3.1 to .net 5, but i notice the visual studio return an warning (CA1416) on the host.RunAsService().

Describe the bug

The .NET 5 return Platform Not Supported if you try to use .NET 5 as a Windows Service application. Even if the OperatingSystem.IsWindows return true, the application return errors when i try to start on services.msc. This does not happen in .net core 3.1.

To Reproduce

See @eerhardt's notes in https://github.com/dotnet/sdk/issues/16049#issuecomment-785189111 for simplified set of repro steps

To Reproduce Original

Windows Services via IHostBuilder

  1. Create a normal .net 5 project.
  2. Add this refences to allow Windows Services:
  1. On Host.CreateDefaultBuilder add .UseWindowsService()

  2. Main Program looks like this

    public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
    
        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                .UseWindowsService()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
        }
  3. Create service on cmd via sc create

Windows Services via IWebHostBuilder

The process is identical up to point 2, inclusive.

  1. Use this code via WebHost to create Windows Service Run.

    public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().RunAsService();
       }
    public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            return WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((context, config) =>
                {
                })
                .UseStartup<Startup>();
        }
  2. Create service on cmd via sc create

The application will return Exception Info: System.PlatformNotSupportedException: ServiceController enables manipulating and accessing Windows services and it is not applicable for other operating systems.

Further technical details

Visual Studio 16.8.5

.NET SDK (reflecting any global.json):
 Version:   5.0.103
 Commit:    72dec52dbd

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17763
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.103\

Host (useful for support):
  Version: 5.0.3
  Commit:  c636bbdc8a

.NET SDKs installed:
  3.1.111 [C:\Program Files\dotnet\sdk]
  3.1.406 [C:\Program Files\dotnet\sdk]
  5.0.103 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
davidfowl commented 3 years ago

Is the first pattern working? That's the one you want to be using. The WebHostBuilder windows host should still work but will be considered deprecated since the generic host supports windows services as well.

MycroBeat commented 3 years ago

On .NET Core 3.1, yes, it's working correctly, but on .NET 5 return that error.

davidfowl commented 3 years ago

You have 2 code samples, which one doesn't work on .NET 5?

MycroBeat commented 3 years ago

Both not working on .NET 5

BrennanConroy commented 3 years ago

RunAsService is going to be obsolete and the other method is the recommended approach. Transferring to the Runtime repo for UseWindowsService.

dotnet-issue-labeler[bot] commented 3 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

ghost commented 3 years ago

Tagging subscribers to this area: @eerhardt, @maryamariyan See info in area-owners.md if you want to be subscribed.

Issue Details
Hello. Recently, i wanted to upgrade my websites from .net core 3.1 to .net 5, but i notice the visual studio return an warning (CA1416) on the host.RunAsService(). ### Describe the bug The .NET 5 return Platform Not Supported if you try to use .NET 5 as a Windows Service application. Even if the `OperatingSystem.IsWindows` return true, the application return errors when i try to start on services.msc. This does not happen in .net core 3.1. ### To Reproduce Windows Services via `IHostBuilder` 1. Create a normal .net 5 project. 2. Add this refences to allow Windows Services: - Microsoft.AspNetCore.Hosting.WindowsServices - Microsoft.AspNetCore - Microsoft.Extensions.Hosting.WindowsServices 3. On `Host.CreateDefaultBuilder` add .UseWindowsService() 4. Main Program looks like this ``` public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) .UseWindowsService() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } ``` 5. Create service on cmd via `sc create` Windows Services via `IWebHostBuilder` The process is identical up to point 2, inclusive. 3. Use this code via WebHost to create Windows Service Run. ``` public static void Main(string[] args) { CreateWebHostBuilder(args).Build().RunAsService(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) { return WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { }) .UseStartup(); } ``` 4. Create service on cmd via `sc create` The application will return `Exception Info: System.PlatformNotSupportedException: ServiceController enables manipulating and accessing Windows services and it is not applicable for other operating systems.` ### Further technical details Visual Studio 16.8.5 ``` .NET SDK (reflecting any global.json): Version: 5.0.103 Commit: 72dec52dbd Runtime Environment: OS Name: Windows OS Version: 10.0.17763 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.103\ Host (useful for support): Version: 5.0.3 Commit: c636bbdc8a .NET SDKs installed: 3.1.111 [C:\Program Files\dotnet\sdk] 3.1.406 [C:\Program Files\dotnet\sdk] 5.0.103 [C:\Program Files\dotnet\sdk] .NET runtimes installed: Microsoft.AspNetCore.All 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] ```
Author: MycroBeat
Assignees: -
Labels: `area-Extensions-Hosting`, `untriaged`
Milestone: -
eerhardt commented 3 years ago

@MycroBeat - this is working fine for me locally. Do you have a repro project and exact steps to run to reproduce?

One thing to note is that if you are copying the application around, you need to ensure you copy the runtimes folder with the application. System.ServiceProcess.ServiceController.dll is a bit special in that there is a Windows-specific build, which works. And a "default" build which throws PNSE. You want to ensure you are loading and running the Windows-specific version of the assembly that gets placed here:

image

You can try debugging your application by putting System.Diagnostics.Debugger.Launch(); as the first line in Main, and then starting the app under Windows Services. This will prompt you to attach a debugger. Then you can continue the app, and look in the "Modules" window of Visual Studio to see which path/version of that assembly is getting loaded.

MycroBeat commented 3 years ago

The repo is a normal .NET 5 Template, without any modification. I just create template on Visual Studio, then I took the steps exactly as I said in my original post. Then i publish the application with the following configuration:

Target location: bin\Release\net5.0\publish\ Delete existing files: False Configuration: Relase Target Framework: net5.0 Target runtime: Portable

Now I found where the error is, although it is a strange error.

eerhardt commented 3 years ago

Publishing Portable works for me as well:

image

Can you .zip up your publish folder and post it on this issue? That might help me spot the issue. It may be a .deps.json issue.

MycroBeat commented 3 years ago

Yes. Very weird.. Now I published first as Portable and then i republish (i don't know if visual studio delete all .dll files) with Win-x64 and after i published using Win-x64 over „Portable„ throw the error again.

Link

eerhardt commented 3 years ago

Thanks for the .zip of your publish directory. It definitely makes it easier to understand what is happening.

Using your instructions, I now understand the difference and am able to reproduce the problem myself.

Using .csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
  </ItemGroup>
</Project>

and Program.cs:

using System;
using System.ServiceProcess;

namespace Repro48620
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(new Service());
        }

        class Service : ServiceBase
        {
        }
    }
}
  1. "Folder" Publish with the default settings:

image

  1. Now change "Target runtime" to win-x64, leave 'Framework-dependent` the same, and publish again

image

  1. Run the published app, and you get:
Unhandled exception. System.PlatformNotSupportedException: ServiceController enables manipulating and accessing Windows services and it is not applicable for other operating systems.
   at System.ServiceProcess.ServiceBase..ctor()
   at Repro48620.Program.Service..ctor()
   at Repro48620.Program.Main(String[] args) in C:\Users\eerhardt\source\repos\Repro48620\Repro48620\Program.cs:line 10

Notice that if you delete the bin and obj folders and just publish directly for win-x64, and run the published app, it works just fine. So for now, my suggestion is to ensure your publish directory is cleaned before publishing. That will help you avoid the problem.

The issue is that the first "portable" Publish is laying down the PNSE version of System.ServiceProcess.ServiceController.dll directly in the publish directory, which is correct because this is a "portable" app. The windows-specific version is going into runtimes\win\lib\netstandard2.0, where it is getting picked up correctly at runtime.

However, the 2nd framework-dependent / win-x64 publish is not overwriting the PNSE version of the file. But it should be, because this is no longer a "portable" publish. It is windows specific. So the windows specific assembly should be going directly to the publish folder, and not into a runtimes\... folder. I'm not exactly sure why publish isn't working correctly.

Since this is a "publish" bug, and not a runtime bug, I'm moving it to dotnet/sdk.

cc @dsplaisted

dotnet-issue-labeler[bot] commented 3 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

davidfowl commented 3 years ago

cc @vijayrkn

vijayrkn commented 3 years ago

@eerhardt - Does it repro will 'dotnet publish' in commandline as well or is it specific to VS?

eerhardt commented 3 years ago

It repros as well on the commandline, but it is a bit harder to make it happen:

❯ dotnet publish -c Release -o C:\temp\Repro48620
❯ dotnet publish -c Release -r win-x64 -o C:\temp\Repro48620
❯ C:\temp\Repro48620\Repro48620.exe
Unhandled exception. System.PlatformNotSupportedException: ServiceController enables manipulating and accessing Windows services and it is not applicable for other operating systems.
   at System.ServiceProcess.ServiceBase..ctor()
   at Repro48620.Program.Service..ctor()
   at Repro48620.Program.Main(String[] args) in C:\Users\eerhardt\source\repos\Repro48620\Repro48620\Program.cs:line 10

In order to repro it on the command line you need to publish to the same directory both times (by default they will go to different directories since you are setting -r).

vijayrkn commented 3 years ago

@eerhardt - Ah! that makes sense. Now I see why it surfaces more in the VS case since the same profile is updated. Do you know if 2 dlls have the same timestamp? May be msbuild is just skipping because it might be set to 'update only if newer'.

vijayrkn commented 3 years ago

@marcpopMSFT - Based on the above response, I think some one from the sdk needs to take a look. This issue is not specific to VS. May be 'dotnet publish' should always overwrite all files. /cc @dsplaisted

marcpopMSFT commented 3 years ago

@vijayrkn Should the UI change the target location as you change your target runtime? The command line repro while simple, is fairly niche and so may not be something we investigate.

vijayrkn commented 3 years ago

@marcpopMSFT - I think this is not about the UI but about the correctness of a publish operation. Irrespective of what location the bits are copied to, I think it is the job of publish to copy the correct bits.

I understand it is more common in VS because of the same location but I don't think that should be the reason to move it out.

marcpopMSFT commented 3 years ago

@vijayrkn if the issue is that the timestamps affect the order here, then this is the behavior of incremental publish that is hard for us to fix without getting rid of incremental publish. We're not sure how we'd fix it but the UI could specify different folders to ensure the customer doesn't hit this in that flow. So basically, we're not sure we can fix it.

vijayrkn commented 3 years ago

Is it possible to introduce a force flag that would overwrite all files irrespective of the timestamp. This way we can retain the incremental behavior by default but allows for the "correct" behavior as well.

I don't know if incremental behavior is valuable by itself if we can't guarantee correctness. We can definitely make the UI changes on the VS side to reduce the impact surface area.

MycroBeat commented 3 years ago

Hello again. Thanks to all the Microsoft employees who worked to fix this issue. With the latest stable version of Visual Studio (16.9.2 when i write this) this error is fixed, but I find another publish bug in this release.

The project is the same, but the publish method is different.

Target location: C:\Test Delete existing files: False Configuration: Release Target Framework: net5.0 Target runtime: win-x86

If i publish using this publish configuration and the application is set to use Framework-Dependent, the application return:

Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'C:\Test\WebApplication1.dll'. An attempt was made to load a program with an incorrect format.

I also tried to publish via command line using this command line dotnet publish -r win-x86 -c Release -o "C:\Test\", which is the command for Self-Contained the application returns the error below, even if the dll is present. (This error also is returned if i'm using Self-Contained on the GUI)

Failed to load the dll from [C:\Test\hostpolicy.dll], HRESULT: 0x800700C1
An error occurred while loading required library hostpolicy.dll from [C:\Test\]
marcpopMSFT commented 3 years ago

@MycroBeat if you delete all files in the c:\test folder and then publish, do you still see this issue? It may just be another symptom of the same issue where we assume you are publishing to different folders and so do not always overwrite/clean the target folder.

MycroBeat commented 3 years ago

@marcpopMSFT Yes, i still see that issue.

Also, i run more tests on Framework Dependent and Self-Contained and i discovered this:

  1. Using Framework Dependent on Deployment Mode
    • a) Without manually delete all files: Portable: Working win-x64: Working win-x86: Returns System.BadImageFormatException (error described by me)
  1. Using Self-Contained on Deployment Mode
    • a) Without manually delete all files: win-x64: Working win-x86: Returns Failed to load the dll from [C:\Test\hostpolicy.dll], HRESULT: 0x800700C1 (error described by me)
marcpopMSFT commented 3 years ago

To clarify, are you deleting the files in between each publish? For performance reasons, we do not overwrite every file in the publish folder as those are typically different folders. What about if you just publish to different directories for each RID?

MycroBeat commented 3 years ago

@marcpopMSFT Yes, all files has been deleted after each publish.

What about if you just publish to different directories for each RID

Unfortunately, I get the same result as the one described above depending on deployment mode.

marcpopMSFT commented 3 years ago

Can you try deleting the obj folder in between executions as well to narrow this down? We think maybe it's picking up a file from there instead.

MycroBeat commented 3 years ago

Unfortunately, I get the same result. See a video below.

https://we.tl/t-mgFvD1d9wc

marcpopMSFT commented 3 years ago

Ok, with your latest video we were able to sort out what's happening as the original publish to the same folder was a red herring. When you publish for x86, we expect you to run the project using the .exe. You can use dotnet.exe to run it but you have to use the dotnet.exe that matches the bitness you are trying to run and it's less recommended for a published self-contained application.

So in your case, you can run WebApplication1.exe or run "\Program Files (x86)\dotnet\dotnet.exe" WebApplication1.dll (if you have the x86 version installed). Do those options work for you?

MycroBeat commented 3 years ago

When you publish for x86, we expect you to run the project using the .exe.

I haven't noticed that I can use .exe to start the web application. I only knew about Windows Services, putting the application as a service.

So in your case, you can run WebApplication1.exe or run "\Program Files (x86)\dotnet\dotnet.exe" WebApplication1.dll (if you have the x86 version installed). Do those options work for you?

Yes. If i run the x86 binary, the both deployment modes it's working correctly.

2021-4-15 3-18-33

marcpopMSFT commented 3 years ago

We switched to including an exe file for all .net Core builds in 3.1 I believe and it's really just a host for the .dll that sits next to it. Thanks for confirming that using the x86 dotnet.exe worked. If you're now unblocked, can this issue be closed?

MycroBeat commented 3 years ago

Yeah, this issue can now be closed. Thank everyone for your help. <3

micktion commented 2 years ago

OK can someone basically summarise what it is you have to do to get this to work... I've been reading this thread for about 5 minutes and I'm still not there.

micktion commented 2 years ago

OK worked it out....

If you publish with "Framework-dependent" then realise you should have picked "Self-contained", before you publish again you need to delete the contents of the Publish folder using file explorer.

Visual Studio for other project types usually has an option to delete the contents before publishing... however there's no such option in this Visual Studio dialog...

image