[fix]Config创建默认配置文件的开关Runtime.CreateConfigOnMissing,仅需对自动创建生效,而不应该阻止用户主动Save
智能大石头 authored at 2024-08-09 00:30:41 石头 committed at 2024-08-10 14:22:24
9.42 KiB
X
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.ServiceProcess;
using NewLife;
using System.Text;
using NewLife.Log;

namespace XAgent;

/// <summary>服务助手</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ServiceHelper
{
    #region 服务安装和启动
    /// <summary>Exe程序名</summary>
    public static String ExeName
    {
        get
        {
            //String filename= AppDomain.CurrentDomain.FriendlyName.Replace(".vshost.", ".");
            //if (filename.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) return filename;

            //filename = Assembly.GetExecutingAssembly().Location;
            //return filename;
            //String filename = Assembly.GetEntryAssembly().Location;
            var p = Process.GetCurrentProcess();
            var filename = p.MainModule.FileName;
            filename = Path.GetFileName(filename);
            filename = filename.Replace(".vshost.", ".");
            return filename;
        }
    }

    /// <summary>安装、卸载 服务</summary>
    /// <param name="service">服务对象</param>
    /// <param name="isinstall">是否安装</param>
    public static void Install(this IAgentService service, Boolean isinstall = true)
    {
        var name = service.ServiceName;
        if (String.IsNullOrEmpty(name)) throw new Exception("未指定服务名!");

        name = name.Replace(" ", "_");
        // win7及以上系统时才提示
        if (Environment.OSVersion.Version.Major >= 6) WriteLine("在win7/win2008及更高系统中,可能需要管理员权限执行才能安装/卸载服务。");
        if (isinstall)
        {
            RunSC("create " + name + " BinPath= \"" + ExeName.GetFullPath() + " -s\" start= auto DisplayName= \"" + service.DisplayName + "\"");
            if (!String.IsNullOrEmpty(service.Description)) RunSC("description " + name + " \"" + service.Description + "\"");
        }
        else
        {
            service.ControlService(false);

            RunSC("Delete " + name);
        }
    }

    /// <summary>启动、停止 服务</summary>
    /// <param name="service">服务对象</param>
    /// <param name="isstart"></param>
    public static void ControlService(this IAgentService service, Boolean isstart = true)
    {
        var name = service.ServiceName;
        if (String.IsNullOrEmpty(name)) throw new Exception("未指定服务名!");

        if (isstart)
            RunCmd("net start " + name, false, true);
        else
            RunCmd("net stop " + name, false, true);
    }

    /// <summary>重启服务</summary>
    /// <param name="service">服务</param>
    public static Boolean Restart(this IAgentService service)
    {
        var serviceName = service.ServiceName;
        XTrace.WriteLine("{0}.Restart {1}", service.GetType().Name, serviceName);

#if !NETSTANDARD
        if (!IsAdministrator()) return RunAsAdministrator("-restart");
#endif

        var cmd = $"/c net stop {serviceName} & ping 127.0.0.1 -n 5 & net start {serviceName}";
        Process.Start("cmd.exe", cmd);

        return true;
    }

    /// <summary>执行一个命令</summary>
    /// <param name="cmd"></param>
    /// <param name="showWindow"></param>
    /// <param name="waitForExit"></param>
    internal static void RunCmd(String cmd, Boolean showWindow, Boolean waitForExit)
    {
        WriteLine("RunCmd " + cmd);

        var p = new Process();
        var si = new ProcessStartInfo();
        var path = Environment.SystemDirectory;
        path = Path.Combine(path, @"cmd.exe");
        si.FileName = path;
        if (!cmd.StartsWith(@"/")) cmd = @"/c " + cmd;
        si.Arguments = cmd;
        si.UseShellExecute = false;
        si.CreateNoWindow = !showWindow;
        si.RedirectStandardOutput = true;
        si.RedirectStandardError = true;
        p.StartInfo = si;

        p.Start();
        if (waitForExit)
        {
            p.WaitForExit();

            var str = p.StandardOutput.ReadToEnd();
            if (!String.IsNullOrEmpty(str)) WriteLine(str.Trim(new Char[] { '\r', '\n', '\t' }).Trim());
            str = p.StandardError.ReadToEnd();
            if (!String.IsNullOrEmpty(str)) WriteLine(str.Trim(new Char[] { '\r', '\n', '\t' }).Trim());
        }
    }

    /// <summary>执行SC命令</summary>
    /// <param name="cmd"></param>
    internal static void RunSC(String cmd)
    {
        var path = Environment.SystemDirectory;
        path = Path.Combine(path, @"sc.exe");
        if (!File.Exists(path)) path = "sc.exe";
        if (!File.Exists(path)) return;
        RunCmd(path + " " + cmd, false, true);
    }
    #endregion

    #region 服务操作辅助函数
    /// <summary>是否已安装</summary>
    public static Boolean IsInstalled(this IAgentService service) => IsServiceInstalled(service.ServiceName);

    /// <summary>是否已启动</summary>
    public static Boolean IsRunning(this IAgentService service) => IsServiceRunning(service.ServiceName);

    /// <summary>取得服务</summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public static ServiceController GetService(String name)
    {
        var list = new List<ServiceController>(ServiceController.GetServices());
        if (list == null || list.Count < 1) return null;

        //return list.Find(delegate(ServiceController item) { return item.ServiceName == name; });
        foreach (var item in list)
        {
            if (item.ServiceName == name) return item;
        }
        return null;
    }

    /// <summary>是否已安装</summary>
    public static Boolean IsServiceInstalled(String name)
    {
        // 取的时候就抛异常,是不知道是否安装的
        using var control = GetService(name);
        if (control == null) return false;

        try
        {
            // 尝试访问一下才知道是否已安装
            var b = control.CanShutdown;
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>是否已启动</summary>
    public static Boolean IsServiceRunning(String name)
    {
        try
        {
            using var control = GetService(name);
            if (control == null) return false;

            // 尝试访问一下才知道是否已安装
            var b = control.CanShutdown;

            control.Refresh();

            return control.Status == ServiceControllerStatus.Running;
        }
        catch
        {
            return false;
        }
    }

    static Boolean RunAsAdministrator(String argument)
    {
        var exe = ExecutablePath;
        if (exe.IsNullOrEmpty()) return false;

        var startInfo = new ProcessStartInfo
        {
            FileName = exe,
            Arguments = argument,
            Verb = "runas",
            UseShellExecute = true,
        };

        var p = Process.Start(startInfo);
        return !p.WaitForExit(5_000) || p.ExitCode == 0;
    }

    static String _executablePath;
    static String ExecutablePath
    {
        get
        {
            if (_executablePath == null)
            {
                var entryAssembly = Assembly.GetEntryAssembly();
                if (entryAssembly != null)
                {
                    var codeBase = entryAssembly.CodeBase;
                    var uri = new Uri(codeBase);
                    _executablePath = uri.IsFile ? uri.LocalPath + Uri.UnescapeDataString(uri.Fragment) : uri.ToString();
                }
                else
                {
                    var moduleFileNameLongPath = GetModuleFileNameLongPath(new HandleRef(null, IntPtr.Zero));
                    _executablePath = moduleFileNameLongPath.ToString().GetFullPath();
                }
            }

            return _executablePath;
        }
    }

    static StringBuilder GetModuleFileNameLongPath(HandleRef hModule)
    {
        var sb = new StringBuilder(260);
        var num = 1;
        var num2 = 0;
        while ((num2 = GetModuleFileName(hModule, sb, sb.Capacity)) == sb.Capacity && Marshal.GetLastWin32Error() == 122 && sb.Capacity < 32767)
        {
            num += 2;
            var capacity = (num * 260 < 32767) ? (num * 260) : 32767;
            sb.EnsureCapacity(capacity);
        }
        sb.Length = num2;
        return sb;
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern Int32 GetModuleFileName(HandleRef hModule, StringBuilder buffer, Int32 length);

    public static Boolean IsAdministrator()
    {
        var current = WindowsIdentity.GetCurrent();
        var windowsPrincipal = new WindowsPrincipal(current);
        return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
    }
    #endregion

    #region 日志
    /// <summary>写日志</summary>
    /// <param name="format"></param>
    /// <param name="args"></param>
    public static void WriteLine(String format, params Object[] args)
    {
        if (XTrace.Debug) XTrace.WriteLine(format, args);
    }

    /// <summary>写日志</summary>
    /// <param name="msg"></param>
    public static void WriteLine(String msg)
    {
        if (XTrace.Debug) XTrace.WriteLine(msg);
    }
    #endregion
}