diff --git a/NewLife.Agent/IHost.cs b/NewLife.Agent/IHost.cs
index 19579ce..e6674fd 100644
--- a/NewLife.Agent/IHost.cs
+++ b/NewLife.Agent/IHost.cs
@@ -40,6 +40,14 @@ public interface IHost
/// 服务名
Boolean Restart(String serviceName);
+ ///// 安装为自启动。登录进入桌面后启动
+ ///// 服务名
+ ///// 显示名
+ ///// 文件路径
+ ///// 描述信息
+ /////
+ //Boolean InstallAutorun(String serviceName, String displayName, String binPath, String description);
+
/// 开始执行服务
///
void Run(ServiceBase service);
@@ -99,6 +107,14 @@ public abstract class Host : DisposeBase, IHost
/// 服务名
public virtual Boolean Restart(String serviceName) => false;
+ ///// 安装为自启动。登录进入桌面后启动
+ ///// 服务名
+ ///// 显示名
+ ///// 文件路径
+ ///// 描述信息
+ /////
+ //public virtual Boolean InstallAutorun(String serviceName, String displayName, String binPath, String description) => false;
+
/// 开始执行服务
///
public abstract void Run(ServiceBase service);
diff --git a/NewLife.Agent/ServiceBase.cs b/NewLife.Agent/ServiceBase.cs
index 39f398a..034e7da 100644
--- a/NewLife.Agent/ServiceBase.cs
+++ b/NewLife.Agent/ServiceBase.cs
@@ -26,6 +26,9 @@ public abstract class ServiceBase : DisposeBase
/// 描述
public String Description { get; set; }
+
+ /// 是否使用自启动。自启动需要用户登录桌面,默认false使用系统服务
+ public Boolean UseAutorun { get; set; }
#endregion
#region 构造
@@ -109,12 +112,18 @@ protected virtual void Init()
if (Host == null)
{
if (Runtime.Windows)
- Host = new WindowsService { Service = this };
+ {
+ if (UseAutorun)
+ Host = new WindowsAutorun { Service = this };
+ else
+ Host = new WindowsService { Service = this };
+ }
else if (Systemd.Available)
Host = new Systemd { Service = this };
- else Host = RcInit.Available ?
- (IHost)new RcInit { Service = this } :
- throw new NotSupportedException($"不支持该操作系统!");
+ else if (RcInit.Available)
+ Host = new RcInit { Service = this };
+ else
+ throw new NotSupportedException($"不支持该操作系统!");
}
Log = XTrace.Log;
@@ -259,6 +268,9 @@ protected virtual void ProcessMenu()
}
#endregion
break;
+ //case '6':
+ // InstallAutorun();
+ // break;
case '7':
if (WatchDogs.Length > 0) CheckWatchDog();
break;
@@ -312,6 +324,11 @@ protected virtual void ShowMenu()
Console.WriteLine("5 模拟运行 -run");
}
+ //if (Runtime.Windows)
+ //{
+ // Console.WriteLine("6 安装开机自启 -autorun");
+ //}
+
var dogs = WatchDogs;
if (dogs.Length > 0)
{
@@ -594,7 +611,7 @@ private void Install()
exe = args[0].GetFullPath();
}
- var bin = $"{exe} -s";
+ var bin = UseAutorun ? $"{exe} -run" : $"{exe} -s";
// 兼容更多参数做为服务启动,譬如:--urls
if (args.Length > 2)
diff --git a/NewLife.Agent/WindowsAutorun.cs b/NewLife.Agent/WindowsAutorun.cs
new file mode 100644
index 0000000..ea167b1
--- /dev/null
+++ b/NewLife.Agent/WindowsAutorun.cs
@@ -0,0 +1,212 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Security.Principal;
+using System.Text;
+using Microsoft.Win32;
+using NewLife.Log;
+
+namespace NewLife.Agent;
+
+/// Windows自启动
+///
+/// 桌面登录自启动
+///
+public class WindowsAutorun : Host
+{
+ /// 开始执行服务
+ ///
+ public override void Run(ServiceBase service) => throw new NotSupportedException();
+
+ /// 服务是否已安装
+ /// 服务名
+ ///
+#if NET5_0_OR_GREATER
+ [SupportedOSPlatform("windows")]
+#endif
+ public override Boolean IsInstalled(String serviceName)
+ {
+#if NET40_OR_GREATER || NET5_0_OR_GREATER
+ // 在注册表中写入启动配置
+ using var key = Registry.LocalMachine.OpenSubKey("""SOFTWARE\Microsoft\Windows\CurrentVersion\Run""", false);
+
+ var v = key.GetValue(serviceName)?.ToString();
+ return !v.IsNullOrEmpty();
+#else
+ return false;
+#endif
+ }
+
+ /// 服务是否已启动
+ /// 服务名
+ ///
+ public override unsafe Boolean IsRunning(String serviceName) => false;
+
+ /// 安装服务
+ /// 服务名
+ ///
+ ///
+ ///
+ ///
+#if NET5_0_OR_GREATER
+ [SupportedOSPlatform("windows")]
+#endif
+ public override Boolean Install(String serviceName, String displayName, String binPath, String description)
+ {
+ XTrace.WriteLine("{0}.Install {1}, {2}, {3}, {4}", GetType().Name, serviceName, displayName, binPath, description);
+
+#if !NETSTANDARD
+ if (!IsAdministrator()) return RunAsAdministrator("-i");
+#endif
+
+ //// 当前用户的启动目录
+ //var dir = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
+ //if (dir.IsNullOrEmpty()) return false;
+
+ //dir.EnsureDirectory(false);
+
+#if NET40_OR_GREATER || NET5_0_OR_GREATER
+ // 在注册表中写入启动配置
+ using var key = Registry.LocalMachine.OpenSubKey("""SOFTWARE\Microsoft\Windows\CurrentVersion\Run""", true);
+
+ // 添加应用程序到自启动项
+ key.SetValue(serviceName, binPath);
+#endif
+
+ return true;
+ }
+
+ /// 卸载服务
+ /// 服务名
+ ///
+#if NET5_0_OR_GREATER
+ [SupportedOSPlatform("windows")]
+#endif
+ public override unsafe Boolean Remove(String serviceName)
+ {
+ XTrace.WriteLine("{0}.Remove {1}", GetType().Name, serviceName);
+
+#if !NETSTANDARD
+ if (!IsAdministrator()) return RunAsAdministrator("-uninstall");
+#endif
+
+#if NET40_OR_GREATER || NET5_0_OR_GREATER
+ // 在注册表中写入启动配置
+ using var key = Registry.LocalMachine.OpenSubKey("""SOFTWARE\Microsoft\Windows\CurrentVersion\Run""", true);
+ key.DeleteValue(serviceName, false);
+#endif
+
+ return true;
+ }
+
+ /// 查询服务配置
+ /// 服务名
+#if NET5_0_OR_GREATER
+ [SupportedOSPlatform("windows")]
+#endif
+ public override unsafe ServiceConfig QueryConfig(String serviceName)
+ {
+#if NET40_OR_GREATER || NET5_0_OR_GREATER
+ using var key = Registry.LocalMachine.OpenSubKey("""SOFTWARE\Microsoft\Windows\CurrentVersion\Run""", false);
+
+ var v = key.GetValue(serviceName)?.ToString();
+ if (v.IsNullOrEmpty()) return null;
+
+ return new ServiceConfig
+ {
+ Name = serviceName,
+ FilePath = v.TrimEnd(" -run")
+ };
+#else
+ return null;
+#endif
+ }
+
+#if !NETSTANDARD
+ static Boolean RunAsAdministrator(String argument)
+ {
+ var exe = ExecutablePath;
+ if (exe.IsNullOrEmpty()) return false;
+
+ if (exe.EndsWithIgnoreCase(".dll"))
+ {
+ var exe2 = Path.ChangeExtension(exe, ".exe");
+ if (File.Exists(exe2)) exe = exe2;
+ }
+
+ var startInfo = exe.EndsWithIgnoreCase(".dll") ?
+ new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ Arguments = $"{Path.GetFileName(exe)} {argument}",
+ WorkingDirectory = Path.GetDirectoryName(exe),
+ Verb = "runas",
+ UseShellExecute = true,
+ } :
+ 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);
+
+#if NET5_0_OR_GREATER
+ [SupportedOSPlatform("windows")]
+#endif
+ private Boolean IsAdministrator()
+ {
+ var current = WindowsIdentity.GetCurrent();
+ var windowsPrincipal = new WindowsPrincipal(current);
+ return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+#endif
+}
\ No newline at end of file
diff --git a/Test/MyServices.cs b/Test/MyServices.cs
index 86dfe8a..9ad0ca1 100644
--- a/Test/MyServices.cs
+++ b/Test/MyServices.cs
@@ -20,6 +20,8 @@ public MyServices()
DisplayName = "新生命服务代理";
Description = "用于承载各种服务的服务代理!";
+
+ UseAutorun = true;
}
#endregion