diff --git a/src/WinSW.Core/Native/Service.cs b/src/WinSW.Core/Native/Service.cs
index 47915cf..e889c10 100644
--- a/src/WinSW.Core/Native/Service.cs
+++ b/src/WinSW.Core/Native/Service.cs
@@ -139,6 +139,24 @@ namespace WinSW.Native
internal Service(IntPtr handle) => this.handle = handle;
+ internal unsafe int ProcessId
+ {
+ get
+ {
+ if (!QueryServiceStatusEx(
+ this.handle,
+ ServiceStatusType.ProcessInfo,
+ out SERVICE_STATUS_PROCESS status,
+ sizeof(SERVICE_STATUS_PROCESS),
+ out _))
+ {
+ Throw.Command.Win32Exception("Failed to query service status.");
+ }
+
+ return status.CurrentState == ServiceControllerStatus.Running ? status.ProcessId : -1;
+ }
+ }
+
///
internal ServiceControllerStatus Status
{
diff --git a/src/WinSW.Core/Native/ServiceApis.cs b/src/WinSW.Core/Native/ServiceApis.cs
index 0f7877a..ae59139 100644
--- a/src/WinSW.Core/Native/ServiceApis.cs
+++ b/src/WinSW.Core/Native/ServiceApis.cs
@@ -65,6 +65,14 @@ namespace WinSW.Native
[DllImport(Libraries.Advapi32, SetLastError = true)]
internal static extern bool QueryServiceStatus(IntPtr serviceHandle, out SERVICE_STATUS serviceStatus);
+ [DllImport(Libraries.Advapi32, SetLastError = true)]
+ internal static extern bool QueryServiceStatusEx(
+ IntPtr serviceHandle,
+ ServiceStatusType infoLevel,
+ out SERVICE_STATUS_PROCESS buffer,
+ int bufferSize,
+ out int bytesNeeded);
+
[DllImport(Libraries.Advapi32, SetLastError = true)]
internal static extern bool SetServiceObjectSecurity(IntPtr serviceHandle, SecurityInfos securityInformation, byte[] securityDescriptor);
@@ -145,6 +153,12 @@ namespace WinSW.Native
MODIFY_BOOT_CONFIG,
}
+ // SC_STATUS_
+ internal enum ServiceStatusType
+ {
+ ProcessInfo = 0,
+ }
+
internal struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool DelayedAutostart;
@@ -182,5 +196,18 @@ namespace WinSW.Native
public int CheckPoint;
public int WaitHint;
}
+
+ internal struct SERVICE_STATUS_PROCESS
+ {
+ public ServiceType ServiceType;
+ public ServiceControllerStatus CurrentState;
+ public int ControlsAccepted;
+ public int Win32ExitCode;
+ public int ServiceSpecificExitCode;
+ public int CheckPoint;
+ public int WaitHint;
+ public int ProcessId;
+ public int ServiceFlags;
+ }
}
}
diff --git a/src/WinSW.Core/Util/ProcessExtensions.cs b/src/WinSW.Core/Util/ProcessExtensions.cs
index f76a369..f89bdc8 100644
--- a/src/WinSW.Core/Util/ProcessExtensions.cs
+++ b/src/WinSW.Core/Util/ProcessExtensions.cs
@@ -15,20 +15,72 @@ namespace WinSW.Util
{
Stop(process, stopTimeout);
- foreach (Process child in GetDescendants(process))
+ foreach (Process child in GetChildren(process))
{
- StopTree(child, stopTimeout);
+ using (child)
+ {
+ StopTree(child, stopTimeout);
+ }
}
}
internal static void StopDescendants(this Process process, TimeSpan stopTimeout)
{
- foreach (Process child in GetDescendants(process))
+ foreach (Process child in GetChildren(process))
{
- StopTree(child, stopTimeout);
+ using (child)
+ {
+ StopTree(child, stopTimeout);
+ }
}
}
+ internal static unsafe List GetChildren(this Process process)
+ {
+ DateTime startTime = process.StartTime;
+ int processId = process.Id;
+
+ var children = new List();
+
+ foreach (Process other in Process.GetProcesses())
+ {
+ try
+ {
+ if (other.StartTime <= startTime)
+ {
+ goto Next;
+ }
+
+ IntPtr handle = other.Handle;
+
+ if (NtQueryInformationProcess(
+ handle,
+ PROCESSINFOCLASS.ProcessBasicInformation,
+ out PROCESS_BASIC_INFORMATION information,
+ sizeof(PROCESS_BASIC_INFORMATION)) != 0)
+ {
+ goto Next;
+ }
+
+ if ((int)information.InheritedFromUniqueProcessId == processId)
+ {
+ Logger.Info($"Found child process '{other.Format()}'.");
+ children.Add(other);
+ continue;
+ }
+
+ Next:
+ other.Dispose();
+ }
+ catch (Exception e) when (e is InvalidOperationException || e is Win32Exception)
+ {
+ other.Dispose();
+ }
+ }
+
+ return children;
+ }
+
private static void Stop(Process process, TimeSpan stopTimeout)
{
Logger.Info("Stopping process " + process.Id);
@@ -61,51 +113,5 @@ namespace WinSW.Util
// TODO: Propagate error if process kill fails? Currently we use the legacy behavior
}
-
- private static unsafe List GetDescendants(Process root)
- {
- DateTime startTime = root.StartTime;
- int processId = root.Id;
-
- var children = new List();
-
- foreach (Process process in Process.GetProcesses())
- {
- try
- {
- if (process.StartTime <= startTime)
- {
- goto Next;
- }
-
- IntPtr handle = process.Handle;
-
- if (NtQueryInformationProcess(
- handle,
- PROCESSINFOCLASS.ProcessBasicInformation,
- out PROCESS_BASIC_INFORMATION information,
- sizeof(PROCESS_BASIC_INFORMATION)) != 0)
- {
- goto Next;
- }
-
- if ((int)information.InheritedFromUniqueProcessId == processId)
- {
- Logger.Info($"Found child process '{process.Format()}'.");
- children.Add(process);
- continue;
- }
-
- Next:
- process.Dispose();
- }
- catch (Exception e) when (e is InvalidOperationException || e is Win32Exception)
- {
- process.Dispose();
- }
- }
-
- return children;
- }
}
}
diff --git a/src/WinSW/Program.cs b/src/WinSW/Program.cs
index 93e188d..fd13eb7 100644
--- a/src/WinSW/Program.cs
+++ b/src/WinSW/Program.cs
@@ -26,6 +26,7 @@ using log4net.Core;
using log4net.Layout;
using WinSW.Logging;
using WinSW.Native;
+using WinSW.Util;
using Process = System.Diagnostics.Process;
using TimeoutException = System.ServiceProcess.TimeoutException;
@@ -238,6 +239,22 @@ namespace WinSW
root.Add(refresh);
}
+ {
+ var dev = new Command("dev");
+
+ dev.Add(config);
+ dev.Add(noElevate);
+
+ root.Add(dev);
+
+ var ps = new Command("ps")
+ {
+ Handler = CommandHandler.Create(DevPs),
+ };
+
+ dev.Add(ps);
+ }
+
return new CommandLineBuilder(root)
// see UseDefaults
@@ -774,6 +791,58 @@ namespace WinSW
}
}
+ void DevPs(string? pathToConfig, bool noElevate)
+ {
+ XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
+
+ if (!elevated)
+ {
+ Elevate(noElevate);
+ return;
+ }
+
+ using ServiceManager scm = ServiceManager.Open();
+ using Service sc = scm.OpenService(config.Id);
+
+ int processId = sc.ProcessId;
+ if (processId >= 0)
+ {
+ const string Vertical = " \u2502 ";
+ const string Corner = " \u2514\u2500";
+ const string Cross = " \u251c\u2500";
+ const string Space = " ";
+
+ using Process process = Process.GetProcessById(processId);
+ Draw(process, string.Empty, true);
+
+ static void Draw(Process process, string indentation, bool isLastChild)
+ {
+ Console.Write(indentation);
+
+ if (isLastChild)
+ {
+ Console.Write(Corner);
+ indentation += Space;
+ }
+ else
+ {
+ Console.Write(Cross);
+ indentation += Vertical;
+ }
+
+ Console.WriteLine(process.Format());
+
+ List children = process.GetChildren();
+ int count = children.Count;
+ for (int i = 0; i < count; i++)
+ {
+ using Process child = children[i];
+ Draw(child, indentation, i == count - 1);
+ }
+ }
+ }
+ }
+
// [DoesNotReturn]
static void Elevate(bool noElevate)
{