diff --git a/src/WinSW.Core/CommandException.cs b/src/WinSW.Core/CommandException.cs
new file mode 100644
index 0000000..5bb9514
--- /dev/null
+++ b/src/WinSW.Core/CommandException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace WinSW
+{
+ internal sealed class CommandException : Exception
+ {
+ internal CommandException(Exception inner)
+ : base(inner.Message, inner)
+ {
+ }
+
+ internal CommandException(string message)
+ : base(message)
+ {
+ }
+
+ internal CommandException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+ }
+}
diff --git a/src/WinSW.Core/Native/Security.cs b/src/WinSW.Core/Native/Security.cs
index a151aa6..d91e466 100644
--- a/src/WinSW.Core/Native/Security.cs
+++ b/src/WinSW.Core/Native/Security.cs
@@ -7,6 +7,7 @@ namespace WinSW.Native
{
internal static class Security
{
+ ///
///
internal static void AddServiceLogonRight(string userName)
{
@@ -23,7 +24,7 @@ namespace WinSW.Native
}
}
- ///
+ ///
private static IntPtr GetAccountSid(string accountName)
{
int sidSize = 0;
@@ -43,7 +44,7 @@ namespace WinSW.Native
{
if (!LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref domainNameLength, out _))
{
- Throw.Win32Exception("Failed to find the account.");
+ Throw.Command.Win32Exception("Failed to find the account.");
}
return sid;
diff --git a/src/WinSW.Core/Native/Service.cs b/src/WinSW.Core/Native/Service.cs
index aa2f988..85f4e19 100644
--- a/src/WinSW.Core/Native/Service.cs
+++ b/src/WinSW.Core/Native/Service.cs
@@ -1,5 +1,4 @@
using System;
-using System.ComponentModel;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text;
@@ -55,19 +54,19 @@ namespace WinSW.Native
private ServiceManager(IntPtr handle) => this.handle = handle;
- ///
+ ///
internal static ServiceManager Open()
{
IntPtr handle = OpenSCManager(null, null, ServiceManagerAccess.ALL_ACCESS);
if (handle == IntPtr.Zero)
{
- Throw.Win32Exception("Failed to open the service control manager database.");
+ Throw.Command.Win32Exception("Failed to open the service control manager database.");
}
return new ServiceManager(handle);
}
- ///
+ ///
internal Service CreateService(
string serviceName,
string displayName,
@@ -111,19 +110,19 @@ namespace WinSW.Native
password);
if (handle == IntPtr.Zero)
{
- Throw.Win32Exception("Failed to create service.");
+ Throw.Command.Win32Exception("Failed to create service.");
}
return new Service(handle);
}
- ///
+ ///
internal Service OpenService(string serviceName)
{
IntPtr serviceHandle = ServiceApis.OpenService(this.handle, serviceName, ServiceAccess.ALL_ACCESS);
if (serviceHandle == IntPtr.Zero)
{
- Throw.Win32Exception("Failed to open the service.");
+ Throw.Command.Win32Exception("Failed to open the service.");
}
return new Service(serviceHandle);
@@ -158,30 +157,30 @@ namespace WinSW.Native
internal Service(IntPtr handle) => this.handle = handle;
- ///
+ ///
internal ServiceControllerStatus Status
{
get
{
if (!QueryServiceStatus(this.handle, out SERVICE_STATUS status))
{
- Throw.Win32Exception("Failed to query service status.");
+ Throw.Command.Win32Exception("Failed to query service status.");
}
return status.CurrentState;
}
}
- ///
+ ///
internal void Delete()
{
if (!DeleteService(this.handle))
{
- Throw.Win32Exception("Failed to delete service.");
+ Throw.Command.Win32Exception("Failed to delete service.");
}
}
- ///
+ ///
internal void SetDescription(string description)
{
if (!ChangeServiceConfig2(
@@ -189,11 +188,11 @@ namespace WinSW.Native
ServiceConfigInfoLevels.DESCRIPTION,
new SERVICE_DESCRIPTION { Description = description }))
{
- Throw.Win32Exception("Failed to configure the description.");
+ Throw.Command.Win32Exception("Failed to configure the description.");
}
}
- ///
+ ///
internal unsafe void SetFailureActions(TimeSpan failureResetPeriod, SC_ACTION[] actions)
{
fixed (SC_ACTION* actionsPtr = actions)
@@ -210,12 +209,12 @@ namespace WinSW.Native
Actions = actionsPtr,
}))
{
- Throw.Win32Exception("Failed to configure the failure actions.");
+ Throw.Command.Win32Exception("Failed to configure the failure actions.");
}
}
}
- ///
+ ///
internal void SetDelayedAutoStart(bool enabled)
{
if (!ChangeServiceConfig2(
@@ -223,18 +222,18 @@ namespace WinSW.Native
ServiceConfigInfoLevels.DELAYED_AUTO_START_INFO,
new SERVICE_DELAYED_AUTO_START_INFO { DelayedAutostart = enabled }))
{
- Throw.Win32Exception("Failed to configure the delayed auto-start setting.");
+ Throw.Command.Win32Exception("Failed to configure the delayed auto-start setting.");
}
}
- ///
+ ///
internal void SetSecurityDescriptor(RawSecurityDescriptor securityDescriptor)
{
byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength];
securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0);
if (!SetServiceObjectSecurity(this.handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes))
{
- Throw.Win32Exception("Failed to configure the security descriptor.");
+ Throw.Command.Win32Exception("Failed to configure the security descriptor.");
}
}
diff --git a/src/WinSW.Core/Native/Throw.cs b/src/WinSW.Core/Native/Throw.cs
index 91cd3a4..b6125fa 100644
--- a/src/WinSW.Core/Native/Throw.cs
+++ b/src/WinSW.Core/Native/Throw.cs
@@ -6,14 +6,17 @@ namespace WinSW.Native
{
internal static class Throw
{
- ///
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal static void Win32Exception(string message)
+ internal static class Command
{
- Win32Exception inner = new Win32Exception();
- Debug.Assert(inner.NativeErrorCode != 0);
- Debug.Assert(message.EndsWith("."));
- throw new Win32Exception(inner.NativeErrorCode, message + ' ' + inner.Message);
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static void Win32Exception(string message)
+ {
+ Win32Exception inner = new Win32Exception();
+ Debug.Assert(inner.NativeErrorCode != 0);
+ Debug.Assert(message.EndsWith("."));
+ throw new CommandException(message + ' ' + inner.Message, inner);
+ }
}
}
}
diff --git a/src/WinSW/Program.cs b/src/WinSW/Program.cs
index 2d70599..ead3ba0 100644
--- a/src/WinSW/Program.cs
+++ b/src/WinSW/Program.cs
@@ -40,6 +40,20 @@ namespace WinSW
Console.Error.WriteLine(message);
return -1;
}
+ catch (CommandException e)
+ {
+ string message = e.Message;
+ Log.Fatal(message);
+ Console.Error.WriteLine(message);
+ return e.InnerException is Win32Exception inner ? inner.NativeErrorCode : -1;
+ }
+ catch (InvalidOperationException e) when (e.InnerException is Win32Exception inner)
+ {
+ string message = e.Message;
+ Log.Fatal(message, e);
+ Console.Error.WriteLine(message);
+ return inner.NativeErrorCode;
+ }
catch (Win32Exception e)
{
string message = e.Message;
@@ -47,13 +61,6 @@ namespace WinSW
Console.Error.WriteLine(message);
return e.NativeErrorCode;
}
- catch (UserException e)
- {
- string message = e.Message;
- Log.Fatal(message, e);
- Console.Error.WriteLine(message);
- return -1;
- }
catch (Exception e)
{
Log.Fatal("Unhandled exception", e);
@@ -186,7 +193,7 @@ namespace WinSW
{
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
- throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists");
+ throw new CommandException("Installation failure: Service with id '" + descriptor.Id + "' already exists");
}
string? username = null;
@@ -292,7 +299,7 @@ namespace WinSW
inBuffer,
ref inBufferSize))
{
- Throw.Win32Exception("Failed to pack auth buffer.");
+ Throw.Command.Win32Exception("Failed to pack auth buffer.");
}
CredentialApis.CREDUI_INFO info = new CredentialApis.CREDUI_INFO
@@ -348,7 +355,7 @@ namespace WinSW
password,
ref passwordLength))
{
- Throw.Win32Exception("Failed to unpack auth buffer.");
+ Throw.Command.Win32Exception("Failed to unpack auth buffer.");
}
}
finally
@@ -404,9 +411,9 @@ namespace WinSW
sc.Delete();
}
- catch (Win32Exception e)
+ catch (CommandException e) when (e.InnerException is Win32Exception inner)
{
- switch (e.NativeErrorCode)
+ switch (inner.NativeErrorCode)
{
case Errors.ERROR_SERVICE_DOES_NOT_EXIST:
Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall");
@@ -420,8 +427,8 @@ namespace WinSW
break; // it's already uninstalled, so consider it a success
default:
- Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
- throw e;
+ Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. Error code is '" + inner.NativeErrorCode + "'");
+ throw;
}
}
}
@@ -588,7 +595,7 @@ namespace WinSW
if (!ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _))
{
- throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());
+ throw new CommandException("Failed to invoke restart: " + Marshal.GetLastWin32Error());
}
}
@@ -668,10 +675,10 @@ namespace WinSW
}
}
- ///
+ ///
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowNoSuchService(Win32Exception inner) => throw new UserException(null, inner);
+ private static void ThrowNoSuchService(Win32Exception inner) => throw new CommandException(inner);
private static void InitLoggers(ServiceDescriptor descriptor, bool enableConsoleLogging)
{
@@ -733,12 +740,13 @@ namespace WinSW
appenders.ToArray());
}
+ ///
internal static bool IsProcessElevated()
{
IntPtr process = ProcessApis.GetCurrentProcess();
if (!ProcessApis.OpenProcessToken(process, TokenAccessLevels.Read, out IntPtr token))
{
- Throw.Win32Exception("Failed to open process token.");
+ Throw.Command.Win32Exception("Failed to open process token.");
}
try
@@ -752,7 +760,7 @@ namespace WinSW
sizeof(SecurityApis.TOKEN_ELEVATION),
out _))
{
- Throw.Win32Exception("Failed to get token information.");
+ Throw.Command.Win32Exception("Failed to get token information.");
}
return elevation.TokenIsElevated != 0;