Merge pull request #362 from NextTurn/pinvoke

Improve P/Invoke signatures
pull/449/head^2
Next Turn 2020-03-25 21:45:39 +08:00 committed by GitHub
commit 6280a688ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 133 deletions

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

@ -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);
} }