目前我是通过以下方式进行检查:
if (Environment.UserInteractive)
Application.Run(new ServiceControllerForm(service));
else
ServiceBase.Run(windowsService);
它可以帮助调试并且可通过可执行文件运行服务。但是现在假设该服务需要与用户桌面进行交互,因此我必须在属性中启用“允许服务与桌面交互”。这当然会破坏此检查方式。是否还有其他方法?
目前我是通过以下方式进行检查:
if (Environment.UserInteractive)
Application.Run(new ServiceControllerForm(service));
else
ServiceBase.Run(windowsService);
它可以帮助调试并且可通过可执行文件运行服务。但是现在假设该服务需要与用户桌面进行交互,因此我必须在属性中启用“允许服务与桌面交互”。这当然会破坏此检查方式。是否还有其他方法?
Environment.UserInteractive
始终返回true
。目前最好的方法似乎是从ASP.NET Core中使用此方法。public static bool IsService()
{
ServiceController sc = new ServiceController("MyApplication");
return sc.Status == ServiceControllerStatus.StartPending;
}
IsService
方法与Environment.UserInteractive
相结合,以获得几乎正确的答案。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
}
IsService
方法如下所示: 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;
}
}
Environment.UserInteractive
更可靠。这是我们在this的帮助下使用的。
bool IsStartupAsService(string serviceName)
{
return ServiceController
.GetServices()
.Where(s => s.ServiceName == serviceName)
.Any(s => s.Status == ServiceControllerStatus.StartPending);
}
// 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);
}
}
}
不要使用 Environment.UserInteractive
属性,而是修改服务的启动方法以检查是否存在“-console”命令行参数。如果存在该参数,则作为普通应用程序运行。否则,作为服务运行。虽然这不像属性检查那样自动化,但很容易添加一个快捷方式到桌面上,使其为您添加“-console”命令行参数。
另外,您需要知道交互式桌面已在 Windows Vista 及以上版本中 被禁用。如果您正在运行需要与用户交互的 Windows 服务,则现在推荐的方法是将前端应用程序与 Windows 服务分离,并使用类似于 WCF 的东西进行通信。
如果您需要调试 Windows 服务(无论它是作为服务还是作为应用程序运行),请在启动方法中调用 System.Diagnostics.Debugging.Break()
。这将强制出现提示,允许您进入调试会话。我经常使用这种技术来调试我的 Windows 服务。
您可以检查进程或其任何父进程是否列为服务:
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;
}
}