Closed DercioGaspar closed 6 years ago
Do any of these log messages end up on disk? it may also help to put a try/catch around the contents of the RunAsService
method and log any exception.
Also, does ThreadManager.Start()
block the method or does it return immediately after kicking off some background work? The IWin32Service.Start
method is required to exit when startup is completed. This exit (without exception) will cause the service host to report the "Started" state of the service. The error message you are seeing usually indicates that this state (or any other state) is not being reported.
Indeed the ThreadManaget.Start()
would block the method, I've changed that to:
Thread thread = new Thread(new ThreadStart(ThreadManager.Start));
thread.Start();
Still the problem persists. The stack I wrote is the result of try/catch right after the Configs.Load();
I tried to debug calling the RunAsService method and the breakpoint I put in the Start method is never reached, went to the Event Viewer and found this:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name=".NET Runtime" />
<EventID Qualifiers="0">1022</EventID>
<Level>2</Level>
<Task>0</Task>
<Keywords>0x80000000000000</Keywords>
<TimeCreated SystemTime="2017-12-06T09:48:52.000000000Z" />
<EventRecordID>50378</EventRecordID>
<Channel>Application</Channel>
<Computer>DGaspar</Computer>
<Security UserID="S-1-5-21-1078081533-436374069-1202660629-9033" />
</System>
<EventData>
<Data>.NET Runtime version 4.0.30319.0 - Loading profiler failed during CoCreateInstance. Profiler CLSID: '{de70e25c-02b5-4556-a837-8aa6fe502668}'. HRESULT: 0x8007007e. Process ID (decimal): 7736. Message ID: [0x2504].</Data>
</EventData>
</Event>
For what I can see it fails on if (!nativeInterop.StartServiceCtrlDispatcherW(serviceTable))
in Win32ServiceHost
class, when I call serviceHost.Run();
If you need any more info, just let me know.
if you start and debug the RunAsService method, it is expected to fail at this point because the native method called here expects that it can connect to the windows service management system, which only works when the process is started by it (== when the process is started because the windows service is started).
As stated in https://github.com/dasMulli/dotnet-win32-service/issues/18#issuecomment-267798630, you could try adding a
while (!Debugger.IsAttached)
{
Thread.Sleep(100);
}
loop to the process, start the windows service via the service management console and attach to it via VS (you'd have to be quick though. this is the only way to really debug a windows service when actually running as service, even with ServiceBase).
The event looks a bit strange - a .NET Core service shouldn't try to load the full .NET Runtimee.
Found out the problem, I was trying to load configs with:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true);
var root = builder.Build();
But apparently it doesn't like those nuggets (Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.FileExtensions and Microsoft.Extensions.Configuration.Json). Guess I'll have to find another way to load my configs without using the IWebHost builder (this service is not gonna do anything related to Web so in my head it doesn't make sense to use it, I may be wrong in my theory tho)
You may want to take a look at the MVC sample which has a feature to preserve the working directory from where the register call is made.
A windows service always starts with the working directory being the system32 directory. So your application was likely looking for a C:\Windows\system32\appsettings.json
file.
Closing since you found the issue(s) specific to your application. Reopen if necessary.
@dasMulli Just in case you know. Is there a good way to load configs?
I do like the configuration system that comes with asp.net core. Using the Microsoft.Extensions.Configuration
NuGet package is useful even in non-asp.net core scenario.
In case of windows services, you'll have too look for the config files next to your application. E.g. get the directory part from typeof(Program).Assembly.Location
and use it for new ConfigurationBuilder().SetBasePath(…)…
.
I've even used this configuration system in combination with .NET Framework 4.7.1's now ConfigBuilders system to determine resolved values from multiple json files when loading a config for a classic ASP.NET app.
@dasMulli Would you imagine that, that last message was what unlocked everything?
Reinstalled the Microsoft.Extensions.Configuration.Json nugget and tried the ConfigurationBuilder().SetBasePath(typeof(Program).Assembly.Location);
and it said "invalid path" (ofc it was duhh), then tried hammering down the path just to see if it worked, IT DID. I think tears were seen in my eyes :)
All of this was only because of the Directory.GetCurrentDirectory()
Ended up loading the configs (as suggested by you) with:
System.Reflection.Assembly assembly = typeof(Program).Assembly;
var builder = new ConfigurationBuilder()
.SetBasePath(assembly.Location.Replace(assembly.ManifestModule.Name, string.Empty))
.AddJsonFile("appsettings.json", false, true);
Thank you very much!
I would suggest adding an example loading the configs with way, because the internet is all over the Directory.GetCurrentDirectory()
and never the typeof(Program).Assembly.Location
Glad you figured it out! I think we all wasted weeks of our lives figuring out such config stuff..
The problem is that even .SetBasePath(Path.GetDirectoryName(typeof(Program).Assembly.Location))
doesn't work in all cases. For example an asp.net core app is run with the project directory as working directory and this line would cause it to look int ..\bin\Debug\netcoreapp2.0\
where there is no config file by default. Only the published output will include one. That's why I chose to pursue the "preserve current working directory" strategy which works saving a config that works from dotnet run
during development and dotnet my.dll
on the published output both incases where running interactively would also work from the same setup.
@dasMulli In my case this code will be more idiot prof (the idiot probably would be me in a few moths), this way there is no need for the --preserve-working-directory
:
string assemblyPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
string path = assemblyPath.Contains(@"\bin\") ? Directory.GetCurrentDirectory() : assemblyPath;
var builder = new ConfigurationBuilder()
.SetBasePath(path)
.AddJsonFile("appsettings.json", false, true);
var root = builder.Build();
As far as I've tested it works both ways
Hello, I've been banging with my head on the wall with this one.
When I try to register the service with "c:\Services\KBBService_PT>dotnet.exe C:\Services\KBBService_PT\KBBService.dll --register-service", it just wont start, but creates it. If I open a cmd (always admin mode) and run it as "c:\Services\KBBService_PT>dotnet.exe C:\Services\KBBService_PT\KBBService.dll --interactive" it runs the service in the console as suposed. If I manually call the service with "--run-as-service" happens the same as when I call the "--register-service". I'm running the latest version of the code, on a windows 10 LTSB, and also tried on a Windows Server 2012 R2.
Bellow is a call stack and error message:
C:\Services\KBBService_PT>dotnet.exe C:\Services\KBBService_PT\KBBService.dll --register-service 2017-12-05 17:36:22.174 - An error ocurred: The service did not respond to the start or control request in a timely fashion
------ STACK TRACE: ------ at DasMulli.Win32.ServiceUtils.ServiceHandle.Start(Boolean throwIfAlreadyRunning) in E:_Git\KBB_MVC\DasMulli.Win32.ServiceUtils\ServiceHandle.cs:line 44
at DasMulli.Win32.ServiceUtils.Win32ServiceManager.DoUpdateService(ServiceHandle existingService, ServiceDefinition serviceDefinition, Boolean startIfNotRunning) in E:_Git\KBB_MVC\DasMulli.Win32.ServiceUtils\Win32ServiceManager.cs:line 193
at DasMulli.Win32.ServiceUtils.Win32ServiceManager.CreateOrUpdateService(ServiceDefinition serviceDefinition, Boolean startImmediately) in E:_Git\KBB_MVC\DasMulli.Win32.ServiceUtils\Win32ServiceManager.cs:line 165
at KBBService.Program.RegisterService() in E:_Git\KBB_MVC\KBBService\Program.cs:line 122
at KBBService.Program.Main(String[] args) in E:_Git\KBB_MVC\KBBService\Program.cs:line 37System.ComponentModel.Win32Exception (0x80004005): The service did not respond to the start or control request in a timely fashion at DasMulli.Win32.ServiceUtils.ServiceHandle.Start(Boolean throwIfAlreadyRunning) in E:_Git\KBB_MVC\DasMulli.Win32.ServiceUtils\ServiceHandle.cs:line 44 at DasMulli.Win32.ServiceUtils.Win32ServiceManager.DoUpdateService(ServiceHandle existingService, ServiceDefinition serviceDefinition, Boolean startIfNotRunning) in E:_Git\KBB_MVC\DasMulli.Win32.ServiceUtils\Win32ServiceManager.cs:line 193 at DasMulli.Win32.ServiceUtils.Win32ServiceManager.CreateOrUpdateService(ServiceDefinition serviceDefinition, Boolean startImmediately) in E:_Git\KBB_MVC\DasMulli.Win32.ServiceUtils\Win32ServiceManager.cs:line 165 at KBBService.Program.RegisterService() in E:_Git\KBB_MVC\KBBService\Program.cs:line 122 at KBBService.Program.Main(String[] args) in E:_Git\KBB_MVC\KBBService\Program.cs:line 37
And bellow my Program.cs (Configs appear to be loading correctly):
If you need any more info, just let me know.