Reputation: 1125
I am trying to have my Wix installer register my ASP.NET Core app as a Windows service during installation.
This is part of my program.wsx
:
<Fragment>
<Component Id="WindowsServiceComponent" Guid="B1234567-89AB-CDEF-0123-456789ABCDEF" Directory="INSTALLFOLDER">
<File Id="MyServiceExe" Source="C:\Navcon\net8.0\win-x64\NAV.LocalService.Function.exe" KeyPath="yes"/>
<!-- Register as Windows Service -->
<ServiceInstall
Id="MyAspNetCoreServiceInstall"
Name="Nav.LocalService"
DisplayName="My ASP.NET Core Service"
Description="Runs the ASP.NET Core Web API as a Windows Service"
Start="auto"
Type="ownProcess"
ErrorControl="ignore"
Account="NT AUTHORITY\NetworkService"
/>
<!-- Ensure service starts after installation -->
<ServiceControl
Id="MyAspNetCoreServiceControl"
Name="Nav.LocalService"
Start="install"
Stop="both"
Remove="uninstall"
Wait="yes"
/>
</Component>
But during installation, I get this error:
Of course, it does ask me during the installation for admin prev. and I click "run as admin" but that doesn't seem to help.
Any idea on what could be wrong?
This is my program.cs
:
var builder = WebApplication.CreateBuilder(args);
int freePort = 0;
// Load default settings
builder.Configuration
.AddJsonFile("local.settings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
builder.Host.UseWindowsService(options =>
{
options.ServiceName = "Nav.LocalService"; // MUST MATCH WiX ServiceInstall Name
});
// Determine user-specific settings file path in AppData
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var userSettingsFilePath = Path.Combine(appDataPath, "LocalService", "local.settings.json");
if (File.Exists(userSettingsFilePath))
{
// if we have a local file for each user, take this instead and leave the other one as backup
builder.Configuration.AddJsonFile(userSettingsFilePath, optional: true, reloadOnChange: true);
}
else
{
// copy local file if it doenst already exist
// Ensure the directory exists
var directoryPath = Path.GetDirectoryName(userSettingsFilePath);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
var sourceFilePath = Path.Combine(Directory.GetCurrentDirectory(), "local.settings.json");
var fileContent = File.ReadAllText(sourceFilePath); // Read the content of the existing file
File.WriteAllText(userSettingsFilePath, fileContent);
builder.Configuration.AddJsonFile(userSettingsFilePath, optional: true, reloadOnChange: true);
Thread.Sleep(100);
}
var getLastUsedPort = builder.Configuration["Values:Application:FunctionAppPort"];
builder.Services
.AddHttpClient()
.AddTransient<NTLMRegisterSender>()
.AddTransient<PrintDocumentAction>()
.AddTransient<OpenDocumentAction>()
.AddTransient<SaveDocumentAction>()
.AddSingleton<LoginStateModel>()
.AddTransient<RegisterController>()
.AddSingleton<NtlmRequestSenderService>()
.AddHostedService<NtlmRequestSenderService>()
.AddHostedService<EndpointTriggerService>()
.AddControllers();
var app = builder.Build();
if (String.IsNullOrEmpty(getLastUsedPort))
{
// no old port available get a new one
freePort = Converters.GetFreePort();
var updater = new ConfigUpdater();
updater.UpdateConfig("Application:FunctionAppPort", freePort.ToString());
}
else
{
// we had a port already, check if its free
var portStillFree = Converters.IsPortAvailable(int.Parse(getLastUsedPort));
if (!portStillFree)
{
// port is no longer free, retrieve new one, write to config and update on BC
freePort = Converters.GetFreePort();
var updater = new ConfigUpdater();
updater.UpdateConfig("Application:FunctionAppPort", freePort.ToString());
//Update new port on BC
using (var scope = app.Services.CreateScope())
{
var ntlmRegisterSender = scope.ServiceProvider.GetRequiredService<NTLMRegisterSender>();
await ntlmRegisterSender.UpdateFieldValue("Port", freePort.ToString());
}
}
else
{
// do nothing, leave everythign as it is
}
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
// Hide the console window
HideConsoleWindow();
app.UseRouting();
app.MapControllers();
Thread.Sleep(1000);
string uripath = builder.Configuration["Values:Application:FunctionAppUrl"] ?? "http://localhost:";
uripath += builder.Configuration["Values:Application:FunctionAppPort"] ?? "7072";
string trayAppPath = Converters.GetPathToWPFApp(builder.Configuration["TrayAppPath"]);
try
{
Process.Start(trayAppPath, uripath);
logger.LogInformation("Successfully started the tray app.");
}
catch (Exception ex)
{
logger.LogWarning(ex, "Could not start the tray app. Check local.settings.json for the correct path.");
}
try
{
app.Run(uripath);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to start server: {ex.Message}");
}
void HideConsoleWindow()
{
IntPtr handle = GetConsoleWindow();
if (handle != IntPtr.Zero)
{
ShowWindow(handle, 0);
}
}
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
Upvotes: 0
Views: 38
Reputation: 36006
When that error dialog is displayed, try to start your service with
net start Nav.LocalService
Your service will fail to start, and you'll now need to debug why (e.g. are files missing, or is there a bug in your service, or something else).
The actual message about not being an admin is (almost) never right and I wish the Windows Installer didn't use it. 99.999% of the time the error is in your service code somewhere.
Upvotes: 1