Merge branch 'master' into ci-fix

pull/449/head
Oleg Nenashev 2020-03-27 01:31:37 +01:00 committed by GitHub
commit aa4e7dc176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 114 additions and 158 deletions

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @oleg-nenashev * @oleg-nenashev @NextTurn

View File

@ -3,7 +3,7 @@ Release Notes
##### Newer releases ##### Newer releases
See [GitHub Releases](https://github.com/kohsuke/winsw/releases) See [GitHub Releases](https://github.com/winsw/winsw/releases)
##### 2.3.0 ##### 2.3.0

View File

@ -1,9 +1,10 @@
winsw: Windows service wrapper in less restrictive license winsw: Windows service wrapper in less restrictive license
========================= =========================
[![Github All Releases](https://img.shields.io/github/downloads/kohsuke/winsw/total.svg)](https://github.com/kohsuke/winsw/releases) [![Github All Releases](https://img.shields.io/github/downloads/winsw/winsw/total.svg)](https://github.com/winsw/winsw/releases)
[![NuGet](https://img.shields.io/nuget/v/WinSW.svg)](https://www.nuget.org/packages/WinSW/) [![NuGet](https://img.shields.io/nuget/v/WinSW.svg)](https://www.nuget.org/packages/WinSW/)
[![Build status](https://ci.appveyor.com/api/projects/status/rspft8kdb33g6cis?svg=true)](https://ci.appveyor.com/project/winsw/winsw) [![Build status](https://ci.appveyor.com/api/projects/status/rspft8kdb33g6cis?svg=true)](https://ci.appveyor.com/project/winsw/winsw)
[![Gitter](https://badges.gitter.im/winsw/winsw.svg)](https://gitter.im/winsw/winsw?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
WinSW is an executable binary, which can be used to wrap and manage a custom process as a Windows service. WinSW is an executable binary, which can be used to wrap and manage a custom process as a Windows service.
Once you download the installation package, you can rename `winsw.exe` to any name, e.g. `myService.exe`. Once you download the installation package, you can rename `winsw.exe` to any name, e.g. `myService.exe`.
@ -14,7 +15,7 @@ See the [project manifest](MANIFEST.md).
### Download ### Download
Starting from WinSW `2.x`, the releases are being hosted on [GitHub](https://github.com/kohsuke/winsw/releases) and [nuget.org](https://www.nuget.org/packages/WinSW/). Starting from WinSW `2.x`, the releases are being hosted on [GitHub](https://github.com/winsw/winsw/releases) and [nuget.org](https://www.nuget.org/packages/WinSW/).
Due to historical reasons, the project also uses [Jenkins Maven repository](https://jenkins.io/index.html) as a secondary source. Due to historical reasons, the project also uses [Jenkins Maven repository](https://jenkins.io/index.html) as a secondary source.
Binaries are available [here](http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/). Binaries are available [here](http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/).

View File

@ -15,7 +15,7 @@ More info about the wrapper is available in the projects GitHub repository.
<authors>WinSW contributors</authors> <authors>WinSW contributors</authors>
<owners>Oleg Nenashev, Kohsuke Kawaguchi</owners> <owners>Oleg Nenashev, Kohsuke Kawaguchi</owners>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/kohsuke/winsw</projectUrl> <projectUrl>https://github.com/winsw/winsw</projectUrl>
<!--<iconUrl>TODO</iconUrl>--> <!--<iconUrl>TODO</iconUrl>-->
<copyright>Copyright (c) 2010-2016 Kohsuke Kawaguchi, Sun Microsystems, Inc., CloudBees, Inc., Oleg Nenashev and other contributors</copyright> <copyright>Copyright (c) 2010-2016 Kohsuke Kawaguchi, Sun Microsystems, Inc., CloudBees, Inc., Oleg Nenashev and other contributors</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
@ -27,7 +27,9 @@ More info about the wrapper is available in the projects GitHub repository.
<files> <files>
<file src="artifacts\WinSW.NET2.exe" target="lib\net20-full\WinSW.NET2.exe" /> <file src="artifacts\WinSW.NET2.exe" target="lib\net20-full\WinSW.NET2.exe" />
<file src="artifacts\WinSW.NET4.exe" target="lib\net40-full\WinSW.NET4.exe" /> <file src="artifacts\WinSW.NET4.exe" target="lib\net40-full\WinSW.NET4.exe" />
<file src="artifacts\WinSW.NET461.exe" target="lib\net461-full\WinSW.NET461.exe" />
<file src="examples\sample-allOptions.xml" target="lib\net20-full\WinSW.NET2.xml" /> <file src="examples\sample-allOptions.xml" target="lib\net20-full\WinSW.NET2.xml" />
<file src="examples\sample-allOptions.xml" target="lib\net40-full\WinSW.NET4.xml" /> <file src="examples\sample-allOptions.xml" target="lib\net40-full\WinSW.NET4.xml" />
<file src="examples\sample-allOptions.xml" target="lib\net461-full\WinSW.NET461.xml" />
</files> </files>
</package> </package>

View File

@ -426,7 +426,7 @@ namespace winsw
_wrapperServiceStatus.waitHint = effectiveWaitHint; _wrapperServiceStatus.waitHint = effectiveWaitHint;
// WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint); // WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
_wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING; _wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING;
Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus); Advapi32.SetServiceStatus(handle, _wrapperServiceStatus);
} }
private void SignalShutdownComplete() private void SignalShutdownComplete()
@ -435,7 +435,7 @@ namespace winsw
_wrapperServiceStatus.checkPoint++; _wrapperServiceStatus.checkPoint++;
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint); // WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
_wrapperServiceStatus.currentState = (int)State.SERVICE_STOPPED; _wrapperServiceStatus.currentState = (int)State.SERVICE_STOPPED;
Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus); Advapi32.SetServiceStatus(handle, _wrapperServiceStatus);
} }
private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin) private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin)
@ -796,8 +796,7 @@ namespace winsw
// run restart from another process group. see README.md for why this is useful. // run restart from another process group. see README.md for why this is useful.
STARTUPINFO si = default; bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, Kernel32.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out _);
if (!result) if (!result)
{ {
throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());

View File

@ -1,59 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace winsw
{
public static class SigIntHelper
{
private const string KERNEL32 = "kernel32.dll";
[DllImport(KERNEL32, SetLastError = true)]
private static extern bool AttachConsole(uint dwProcessId);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)]
private static extern bool FreeConsole();
[DllImport(KERNEL32)]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH
private delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
private enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport(KERNEL32)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
/// <summary>
/// Uses the native funciton "AttachConsole" to attach the thread to the executing process to try to trigger a CTRL_C event (SIGINT). If the application
/// doesn't honor the event and shut down gracefully, the. wait period will time out after 15 seconds.
/// </summary>
/// <param name="process">The process to attach to and send the SIGINT</param>
/// <returns>True if the process shut down successfully to the SIGINT, false if it did not.</returns>
public static bool SendSIGINTToProcess(Process process, TimeSpan shutdownTimeout)
{
if (AttachConsole((uint)process.Id))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
return process.HasExited;
}
return false;
}
}
}

View File

@ -6,9 +6,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
#endif #endif
using System.Xml; using System.Xml;
#if !VNEXT
using log4net; using log4net;
#endif
using winsw.Util; using winsw.Util;
namespace winsw namespace winsw
@ -26,9 +24,7 @@ namespace winsw
basic basic
} }
#if !VNEXT
private static readonly ILog Logger = LogManager.GetLogger(typeof(Download)); private static readonly ILog Logger = LogManager.GetLogger(typeof(Download));
#endif
public readonly string From; public readonly string From;
public readonly string To; public readonly string To;
@ -163,25 +159,57 @@ namespace winsw
throw new WebException("Code defect. Unsupported authentication type: " + Auth); throw new WebException("Code defect. Unsupported authentication type: " + Auth);
} }
string tmpFilePath = To + ".tmp"; bool supportsIfModifiedSince = false;
#if VNEXT if (request is HttpWebRequest httpRequest && File.Exists(To))
using (WebResponse response = await request.GetResponseAsync())
#else
using (WebResponse response = request.GetResponse())
#endif
using (Stream responseStream = response.GetResponseStream())
using (FileStream tmpStream = new FileStream(tmpFilePath, FileMode.Create))
{ {
#if VNEXT supportsIfModifiedSince = true;
await responseStream.CopyToAsync(tmpStream); httpRequest.IfModifiedSince = File.GetLastWriteTime(To);
#elif NET20
CopyStream(responseStream, tmpStream);
#else
responseStream.CopyTo(tmpStream);
#endif
} }
FileHelper.MoveOrReplaceFile(To + ".tmp", To); DateTime lastModified = default;
string tmpFilePath = To + ".tmp";
try
{
#if VNEXT
using (WebResponse response = await request.GetResponseAsync())
#else
using (WebResponse response = request.GetResponse())
#endif
using (Stream responseStream = response.GetResponseStream())
using (FileStream tmpStream = new FileStream(tmpFilePath, FileMode.Create))
{
if (supportsIfModifiedSince)
{
lastModified = ((HttpWebResponse)response).LastModified;
}
#if VNEXT
await responseStream.CopyToAsync(tmpStream);
#elif NET20
CopyStream(responseStream, tmpStream);
#else
responseStream.CopyTo(tmpStream);
#endif
}
FileHelper.MoveOrReplaceFile(To + ".tmp", To);
if (supportsIfModifiedSince)
{
File.SetLastWriteTime(To, lastModified);
}
}
catch (WebException e)
{
if (supportsIfModifiedSince && ((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotModified)
{
Logger.Info($"Skipped downloading unmodified resource '{From}'");
}
else
{
throw;
}
}
} }
#if NET20 #if NET20

View File

@ -35,7 +35,7 @@ namespace winsw.Native
public void Dispose() public void Dispose()
{ {
if (_handle != IntPtr.Zero) if (_handle != IntPtr.Zero)
Advapi32.CloseServiceHandle(_handle); _ = Advapi32.CloseServiceHandle(_handle);
_handle = IntPtr.Zero; _handle = IntPtr.Zero;
} }
} }
@ -71,7 +71,7 @@ namespace winsw.Native
Marshal.StructureToPtr(actions[i], new IntPtr(sfa.lpsaActions.ToInt64() + i * len), false); Marshal.StructureToPtr(actions[i], new IntPtr(sfa.lpsaActions.ToInt64() + i * len), false);
} }
if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa)) if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_FAILURE_ACTIONS, sfa))
throw new Exception("Failed to change the failure actions", new Win32Exception()); throw new Exception("Failed to change the failure actions", new Win32Exception());
} }
finally finally
@ -94,7 +94,7 @@ namespace winsw.Native
fDelayedAutostart = enabled fDelayedAutostart = enabled
}; };
if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, ref settings)) if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, settings))
{ {
throw new Exception("Failed to change the DelayedAutoStart setting", new Win32Exception()); throw new Exception("Failed to change the DelayedAutoStart setting", new Win32Exception());
} }
@ -103,7 +103,7 @@ namespace winsw.Native
public void Dispose() public void Dispose()
{ {
if (Handle != IntPtr.Zero) if (Handle != IntPtr.Zero)
Advapi32.CloseServiceHandle(Handle); _ = Advapi32.CloseServiceHandle(Handle);
Handle = IntPtr.Zero; Handle = IntPtr.Zero;
} }
} }
@ -174,19 +174,16 @@ namespace winsw.Native
// StringBuilder and size for the domain name // StringBuilder and size for the domain name
StringBuilder domainName = new StringBuilder(); StringBuilder domainName = new StringBuilder();
int nameSize = 0; int nameSize = 0;
// account-type variable for lookup
int accountType = 0;
// get required buffer size // get required buffer size
Advapi32.LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType); _ = Advapi32.LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref nameSize, out _);
// allocate buffers // allocate buffers
domainName = new StringBuilder(nameSize); domainName = new StringBuilder(nameSize);
sid = Marshal.AllocHGlobal(sidSize); sid = Marshal.AllocHGlobal(sidSize);
// lookup the SID for the account // lookup the SID for the account
bool result = Advapi32.LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, bool result = Advapi32.LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref nameSize, out _);
ref accountType);
// say what you're doing // say what you're doing
// Console.WriteLine("LookupAccountName result = " + result); // Console.WriteLine("LookupAccountName result = " + result);
@ -195,13 +192,11 @@ namespace winsw.Native
if (!result) if (!result)
{ {
winErrorCode = Kernel32.GetLastError(); winErrorCode = Marshal.GetLastWin32Error();
Console.WriteLine("LookupAccountName failed: " + winErrorCode); Console.WriteLine("LookupAccountName failed: " + winErrorCode);
} }
else else
{ {
// initialize an empty unicode-string
LSA_UNICODE_STRING systemName = default;
// combine all policies // combine all policies
const int access = (int)( const int access = (int)(
LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN | LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
@ -220,18 +215,8 @@ namespace winsw.Native
); );
// initialize a pointer for the policy handle // initialize a pointer for the policy handle
// these attributes are not used, but LsaOpenPolicy wants them to exists
LSA_OBJECT_ATTRIBUTES objectAttributes = new LSA_OBJECT_ATTRIBUTES
{
Length = 0,
RootDirectory = IntPtr.Zero,
Attributes = 0,
SecurityDescriptor = IntPtr.Zero,
SecurityQualityOfService = IntPtr.Zero
};
// get a policy handle // get a policy handle
uint resultPolicy = Advapi32.LsaOpenPolicy(ref systemName, ref objectAttributes, access, out IntPtr policyHandle); uint resultPolicy = Advapi32.LsaOpenPolicy(default, default, access, out IntPtr policyHandle);
winErrorCode = Advapi32.LsaNtStatusToWinError(resultPolicy); winErrorCode = Advapi32.LsaNtStatusToWinError(resultPolicy);
if (winErrorCode != 0) if (winErrorCode != 0)
@ -257,7 +242,7 @@ namespace winsw.Native
Console.WriteLine("LsaAddAccountRights failed: " + winErrorCode); Console.WriteLine("LsaAddAccountRights failed: " + winErrorCode);
} }
Advapi32.LsaClose(policyHandle); _ = Advapi32.LsaClose(policyHandle);
} }
Advapi32.FreeSid(sid); Advapi32.FreeSid(sid);
@ -273,52 +258,56 @@ namespace winsw.Native
/// </summary> /// </summary>
public class Advapi32 public class Advapi32
{ {
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private const string Advapi32LibraryName = "advapi32.dll";
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, IntPtr lpInfo);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "ChangeServiceConfig2W")]
[return: MarshalAs(UnmanagedType.Bool)] internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, in SERVICE_FAILURE_ACTIONS lpInfo);
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, ref SERVICE_FAILURE_ACTIONS sfa);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "ChangeServiceConfig2W")]
[return: MarshalAs(UnmanagedType.Bool)] internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, in SERVICE_DELAYED_AUTO_START lpInfo);
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, ref SERVICE_DELAYED_AUTO_START sfa);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenSCManagerW")]
internal static extern IntPtr OpenSCManager(string? machineName, string? databaseName, uint dwAccess); internal static extern IntPtr OpenSCManager(string? lpMachineName, string? lpDatabaseName, uint dwDesiredAccess);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenServiceW")]
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", SetLastError = true)] [DllImport(Advapi32LibraryName, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseServiceHandle(IntPtr hSCObject); internal static extern bool CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.DLL")] [DllImport(Advapi32LibraryName)]
public static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus); public static extern bool SetServiceStatus(IntPtr hServiceStatus, in SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", PreserveSig = true)] [DllImport(Advapi32LibraryName)]
internal static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, int DesiredAccess, internal static extern uint LsaOpenPolicy(
in LSA_UNICODE_STRING SystemName,
in LSA_OBJECT_ATTRIBUTES ObjectAttributes,
int DesiredAccess,
out IntPtr PolicyHandle); out IntPtr PolicyHandle);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] [DllImport(Advapi32LibraryName, SetLastError = true)]
internal static extern uint LsaAddAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, LSA_UNICODE_STRING[] UserRights, uint CountOfRights); internal static extern uint LsaAddAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, LSA_UNICODE_STRING[] UserRights, uint CountOfRights);
[DllImport("advapi32")] [DllImport(Advapi32LibraryName)]
internal static extern void FreeSid(IntPtr pSid); internal static extern void FreeSid(IntPtr pSid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)] [DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LookupAccountNameW")]
internal static extern bool LookupAccountName(string lpSystemName, string lpAccountName, IntPtr psid, ref int cbsid, StringBuilder domainName, internal static extern bool LookupAccountName(
ref int cbdomainLength, ref int use); string? lpSystemName,
string lpAccountName,
IntPtr psid,
ref int cbsid,
StringBuilder domainName,
ref int cbdomainLength,
out int use);
[DllImport("advapi32.dll")] [DllImport(Advapi32LibraryName)]
internal static extern bool IsValidSid(IntPtr pSid); internal static extern bool IsValidSid(IntPtr pSid);
[DllImport("advapi32.dll", SetLastError = true)] [DllImport(Advapi32LibraryName, SetLastError = true)]
internal static extern uint LsaClose(IntPtr ObjectHandle); internal static extern uint LsaClose(IntPtr ObjectHandle);
[DllImport("advapi32.dll", SetLastError = false)] [DllImport(Advapi32LibraryName, SetLastError = false)]
internal static extern uint LsaNtStatusToWinError(uint status); internal static extern uint LsaNtStatusToWinError(uint status);
} }
@ -584,9 +573,7 @@ namespace winsw.Native
/// </summary> /// </summary>
public int dwResetPeriod; public int dwResetPeriod;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpRebootMsg; public string lpRebootMsg;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpCommand; public string lpCommand;
public int cActions; public int cActions;
public IntPtr/*SC_ACTION[]*/ lpsaActions; public IntPtr/*SC_ACTION[]*/ lpsaActions;
@ -596,7 +583,6 @@ namespace winsw.Native
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SERVICE_DELAYED_AUTO_START public struct SERVICE_DELAYED_AUTO_START
{ {
[MarshalAs(UnmanagedType.Bool)]
public bool fDelayedAutostart; public bool fDelayedAutostart;
} }
} }

View File

@ -9,24 +9,25 @@ namespace winsw.Native
/// </summary> /// </summary>
public class Kernel32 public class Kernel32
{ {
[DllImport("kernel32.dll", SetLastError = true)] public const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
private const string Kernel32LibraryName = "kernel32.dll";
[DllImport(Kernel32LibraryName, SetLastError = true)]
public static extern bool SetStdHandle(int nStdHandle, SafeFileHandle handle); public static extern bool SetStdHandle(int nStdHandle, SafeFileHandle handle);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport(Kernel32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateProcessW")]
public static extern bool CreateProcess( public static extern bool CreateProcess(
string? lpApplicationName, string? lpApplicationName,
string lpCommandLine, string? lpCommandLine,
IntPtr lpProcessAttributes, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, IntPtr lpThreadAttributes,
bool bInheritHandles, bool bInheritHandles,
uint dwCreationFlags, uint dwCreationFlags,
IntPtr lpEnvironment, IntPtr lpEnvironment,
string? lpCurrentDirectory, string? lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo, in STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation); out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern int GetLastError();
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]

View File

@ -2,7 +2,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using log4net; using log4net;
using winsw.Native;
namespace winsw.Util namespace winsw.Util
{ {
@ -10,15 +9,15 @@ namespace winsw.Util
{ {
private static readonly ILog Logger = LogManager.GetLogger(typeof(SigIntHelper)); private static readonly ILog Logger = LogManager.GetLogger(typeof(SigIntHelper));
private const string KERNEL32 = "kernel32.dll"; private const string Kernel32LibraryName = "kernel32.dll";
[DllImport(KERNEL32, SetLastError = true)] [DllImport(Kernel32LibraryName, SetLastError = true)]
private static extern bool AttachConsole(uint dwProcessId); private static extern bool AttachConsole(uint dwProcessId);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)] [DllImport(Kernel32LibraryName, SetLastError = true)]
private static extern bool FreeConsole(); private static extern bool FreeConsole();
[DllImport(KERNEL32)] [DllImport(Kernel32LibraryName)]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add); private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH // Delegate type to be used as the Handler Routine for SCCH
@ -34,8 +33,7 @@ namespace winsw.Util
CTRL_SHUTDOWN_EVENT CTRL_SHUTDOWN_EVENT
} }
[DllImport(KERNEL32)] [DllImport(Kernel32LibraryName)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId); private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
/// <summary> /// <summary>
@ -49,8 +47,8 @@ namespace winsw.Util
if (AttachConsole((uint)process.Id)) if (AttachConsole((uint)process.Id))
{ {
// Disable Ctrl-C handling for our program // Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true); _ = SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0); _ = GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
process.WaitForExit((int)shutdownTimeout.TotalMilliseconds); process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
@ -58,7 +56,7 @@ namespace winsw.Util
bool success = FreeConsole(); bool success = FreeConsole();
if (!success) if (!success)
{ {
long errorCode = Kernel32.GetLastError(); long errorCode = Marshal.GetLastWin32Error();
Logger.Warn("Failed to detach from console. Error code: " + errorCode); Logger.Warn("Failed to detach from console. Error code: " + errorCode);
} }