.UseWindowsService() sets the base path to AppContext.BaseDirectory which ends up being something like C:\Windows\Temp\.net\FileUploader\blc0j22k.v0p when using a single file exe and running as a service. This prevents the appsettings.json files from being loaded if they're saved alongside the exe which I've been told is an acceptable deployment scenario.
Steps to reproduce the behavior:
3.0.0-preview7.19362.4 of package Microsoft.Extensions.Hosting.WindowsServicesappsettings.json with a setting in it.ConfigureServices(ctx, services) call ctx.Configuration.GetValue<string>("SomeValue")dotnet publish --configuration Release --output artifacts /p:PublishSingleFile=true /p:PublishTrimmed=trueappsettings.json will not be loadedThe appsettings.json stored alongside the exe should be loaded when running a single file exe as a service.
I originally was using the following code before I added in the Microsoft.Extensions.Hosting.WindowsServices package which I got from the asp.net core 2.2 docs:
var isService = !(Debugger.IsAttached || args.Contains("--console"));
if (isService)
{
using var process = Process.GetCurrentProcess();
var pathToExe = process.MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
After adding this package I modified the code like so:
var builder = Host.CreateDefaultBuilder()
.UseWindowsService()
.ConfigureServices((ctx, services) =>
{
});
+if (WindowsServiceHelpers.IsWindowsService())
+{
+ using var process = Process.GetCurrentProcess();
+ var pathToExe = process.MainModule.FileName;
+ var pathToContentRoot = Path.GetDirectoryName(pathToExe);
+
+ builder.UseContentRoot(pathToContentRoot);
+}
else
{
builder.UseConsoleLifetime();
}
var host = builder.Build();
Depending on how you publish the application this might not be a bug but rather a documentation note. It seems worth mentioning though.
The previous workaround does not work anymore with 3.0.100 of .NET Core. Now if we execute the application will work as intended, even without the workaround, but if we use it as a service it always breaks when starting the service.
Steps to reproduce the behavior:
ASP.NET Core Web Application from Visual StudioAPI3.0.0UseWindowsService() to the HostBuilder on Program.csdotnet publish -o .\publish -r win-x64 --self-contained -c Release -p:PublishSingleFile=True -p:PublishTrimmed=TrueNew-Service -Name MyTestService -BinaryPathName {full-path-to-your-executable}Start-Service -Name MyTestServiceStart-Service : Service 'MyTestService (MyTestService )' cannot be started due to the following error: Cannot start service MyTestService on computer '.'.Exception Info: System.IO.FileNotFoundException: The configuration file 'appSettings.json' was not found and is not optional. The physical path is 'C:\WINDOWS\TEMP\.net\WebApplication2\hp1o1feq.wdy\appSettings.json'.The appsettings.json stored alongside the exe should be loaded when running a single file exe as a service.

位 dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.0.100
Commit: 04339c3a26
Runtime Environment:
OS Name: Windows
OS Version: 6.3.9600
OS Platform: Windows
RID: win81-x64
Base Path: C:\Program Files\dotnet\sdk\3.0.100\
Host (useful for support):
Version: 3.0.0
Commit: 7d57652f33
.NET Core SDKs installed:
2.1.801 [C:\Program Files\dotnet\sdk]
2.2.401 [C:\Program Files\dotnet\sdk]
3.0.100 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Oof, the issue is that AppContext.BaseDirectory isn't the original directory of the single-file exe, it's the directory into which the content was extracted. We intentionally don't embed the content files in single-file scenarios because for non-Windows Services we want them to stay next to the exe instead of being embedded in it.
We need to think a bit about this scenario. Thoughts @davidfowl ?
@vitek-karas @elinor-fung Is there a way to detect when we've been "expanded" from a single-file exe and to get back the path of the original single-file exe? Perhaps using AppContext.GetData?
If that doesn't work, we're going to have to make sure this design works with the newer single-file plan in 5.0.
@swaroop-sridhar
@anurse To get the directory containing the actual single-file app, you can use: Process.GetCurrentProcess().MainModule.FileName or use platform-specific API such as pinvoke into GetModuleFileNameW(Null, , )
Is there also a way to detect when we're launched as a single-file app? Or would it be reasonable to use Process.GetCurrentProcess().MainModule.FileName as a direct replacement for AppContext.BaseDirectory in all cases? I'd expect the Process API to be incorrect if we are launched from dotnet.exe, right?
Linking https://github.com/dotnet/core-setup/issues/7491 for transparency.
If the runtime adopts the behavior described in the design document this issue could likely be closed without action..
We propose that
AppContext.BaseDirectoryshould always be set to the directory where theAppHostbundle resides. This scheme doesn't provide an obvious mechanism to access the contents of the extraction directory -- by design. The recommended method for accessing content files from the bundle are:
- Do not bundle application data files into the single-exe; instead them next to the bundle. This way, the application binary is a single-file, but not the whole application.
- Embed data files as managed resources into application binary, and access them via resource management APIs.
Yes @dasMulli, the intention in the next version is for the AppContext.BaseDirectory to point to the apphost directory, as noted in the design.
@anurse, I agree that an API to detect whether running from a single-file is useful in certain cases. There's a proposal in the design to add this API in the next version.
I'd definitely prefer solving this via https://github.com/dotnet/core-setup/issues/7491
Yes @dasMulli, the intention in the next version is for the
AppContext.BaseDirectoryto point to the apphost directory, as noted in the design.
Given this, I'm going to mark this as External but leave it open for us to verify as 5.0 develops and we have the new single-file bundling logic to test out.
I think it would be very good if we can set the content root from application.
Something like this :
var builder = Host.CreateDefaultBuilder()
.UseWindowsService(settings =>
{
settings.ContentRoot = Directory.GetCurrentDirectory();
});
@BurhanEyimaya You can, just need to make sure to call is after UserWindowsService AFAIK.
https://github.com/dotnet/extensions/blob/275e691f7e575f208290d1cbb8cb450f4a3a85d6/src/Hosting/Hosting/src/HostingHostBuilderExtensions.cs#L35-L41
Just fyi, Directory.GetCurrentDirectory() will point to %WINDIR%\system32 for windows services so that's why it would be good to have a generic API that tells you the location of whatever is the entry point (being entry point dll file for dotnet path/to/foo.dll or the main module foo.exe for app host with or without single file publishing).
Not blocking release of 5.0 product.
@xt0rted could you please check if this still repros on 5.0? (Dupe of https://github.com/dotnet/runtime/issues/3704)
Closing issue as resolved on .NET 5.0.
@xt0rted if this gets repro on 5.0 we could reopen it again.
Most helpful comment
I'd definitely prefer solving this via https://github.com/dotnet/core-setup/issues/7491
Given this, I'm going to mark this as External but leave it open for us to verify as 5.0 develops and we have the new single-file bundling logic to test out.