Currently I'm checking it in the following way:
if (Envir开发者_如何学JAVAonment.UserInteractive)
Application.Run(new ServiceControllerForm(service));
else
ServiceBase.Run(windowsService);
It helps debugging a little and service can also be run using the executable. But assume now that the service requires interaction with the user desktop so that I have to enable "Allow service to interact with desktop" in the properties. This of course breaks this way of checking. Is there another way?
Also, it must be noted, that Environment.UserInteractive
always returns true
in .NET Core, even if it is running as a Windows Service.
For the time being, best method seems to be this one from ASP.NET Core.
Sources: .NET Core 2.2 .NET Core 3.1
Fixed in .NET 5
It's not perfect, but you could probably do something like this:
public static bool IsService()
{
ServiceController sc = new ServiceController("MyApplication");
return sc.Status == ServiceControllerStatus.StartPending;
}
The idea is that if you run this while your service is still starting up then it will always be in the pending state. If the service isn't installed at all then the method will always return false. It will only fail in the very unlikely corner case that the service is starting and somebody is trying to start it as an application at the same time.
I don't love this answer but I think it is probably the best you can do. Realistically it's not a very good idea to allow the same application to run in either service or application mode - in the long run it will be easier if you abstract all of the common functionality into a class library and just create a separate service app. But if for some reason you really really need to have your cake and eat it too, you could probably combine the IsService
method above with Environment.UserInteractive
to get the correct answer almost all of the time.
In .NET Core, you can determine if the application is running as a Windows Service by using the WindowsServiceHelpers.IsWindowsService() static helper method available in the Microsoft.Extensions.Hosting.WindowsServices NuGet package.
Install-Package Microsoft.Extensions.Hosting.WindowsServices
if (WindowsServiceHelpers.IsWindowsService())
{
// The application is running as a Windows Service
}
else
{
// The application is not running as a Windows Service
}
The issue with the accepted answer is that checking the status of an service that isn't installed will throw. The IsService
Method I'm using looks like this:
private bool IsService(string name)
{
if (!Environment.UserInteractive) return true;
System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(name);
try
{
return sc.Status == System.ServiceProcess.ServiceControllerStatus.StartPending;
}
catch(InvalidOperationException)
{
return false;
}
}
Which should work more reliably than just checking Environment.UserInteractive
Instead of using the Environment.UserInteractive
property, modify the startup method of your service to check for a "-console" command line argument. If the argument is present, then run as an ordinary application. If not, run as a service. It's not as automated as the property check, but it'd be easy to add a shortcut to the desktop that adds the "-console" command line argument for you.
As an aside, you need to be aware that interaction with the desktop has been disabled in Windows Vista and beyond. If you are running a Windows service that needs to interact with the user, the approved way of doing this now is to separate your front-end application from your Windows service and have them communicate using something like WCF.
If you need to debug your Windows service (whether it's running as a service or as an application), put a call to System.Diagnostics.Debugging.Break()
in your startup method. This will force a prompt that allows you to enter a debugging session. I use this technique to debug my Windows service all the time.
Why not just use a command line switch?
// Note that you have to add the params argument,
// which isn't usually present in windows services
private static void Main(params string[] parameters)
{
....
if (parameters.Length > 0)
{
if (parameters[0].ToLower() == "/console")
{
Application.Run(new ServiceControllerForm(service));
{
else
{
ServiceBase.Run(windowsService);
}
}
}
This is what we used with the help of this answer.
bool IsStartupAsService(string serviceName)
{
return ServiceController
.GetServices()
.Where(s => s.ServiceName == serviceName)
.Any(s => s.Status == ServiceControllerStatus.StartPending);
}
You can check if the process or any of its parent processes are listed as a service:
var process = System.Diagnostics.Process.GetCurrentProcess();
var parent = process.Parent();
var procIsService = process?.IsService;
var parentIsService = parent?.IsService;
...
public static class Extensions
{
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
public static bool IsService(this Process process)
{
using (ManagementObjectSearcher Searcher = new ManagementObjectSearcher(
"SELECT * FROM Win32_Service WHERE ProcessId =" + "\"" + process.Id + "\""))
{
foreach (ManagementObject service in Searcher.Get())
return true;
}
return false;
}
}
精彩评论