创建Windows计划任务

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Win32.TaskScheduler;
using Newtonsoft.Json;

namespace MagicScheduledTask
{
    internal class ScheduledTaskBusiness
    {
        /// <summary>
        /// 开始业务
        /// </summary>
        public void Start()
        {
            LogService.Log.Info("计划任务工具启动。");
            // 获取帐户列表
            List<AccountItem> users = AccountHelper.GetAccounts();
            users.RemoveAll(user => user.IsDisabled);
            LogService.Log.Info($"获取帐户列表:{JsonConvert.SerializeObject(users)}");
            // 设置管理员权限
            SetUserAdmin(users);
            // 获取已存在任务列表
            var existsTaskNames = GetRelatedTaskNames();
            // 各账号下的计划任务
            foreach (var user in users)
            {
                // 添加监听帐户变更的计划任务
                TryCreateAccountScheduledTask(user, existsTaskNames);
                // 每个帐户登录时启动Center
                TryCreateWindowsCenterTask(user, existsTaskNames);
                // 每个帐户登录时启动日志收集器,另每日早9点也启动
                TryCreateLogCollectorTask(user, existsTaskNames);
            }
            // 清理冗余的任务
            if (existsTaskNames.Any())
            {
                DeleteTasks(existsTaskNames);
            }
            LogService.Log.Info($"计划任务工具退出。");
        }

        /// <summary>
        /// 创建账号变更计划任务
        /// 注:在当前账号下创建,防止其它账号删除等情况,导致任务无法正常触发
        /// </summary>
        private static void TryCreateAccountScheduledTask(AccountItem user, List<string> toRemoveTasks)
        {
            var taskName = $"{CustomText.ScheduledTaskApp}_{user.Name}";
            var exePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{CustomText.ScheduledTaskApp}.exe");
            if (!File.Exists(exePath))
            {
                LogService.Log.Error($"可执行文件{exePath}不存在");
                return;
            }
            if (toRemoveTasks.Contains(taskName))
            {
                toRemoveTasks.Remove(taskName);
                LogService.Log.Info($"任务{taskName}已存在,不重新创建");
                return;
            }
            ScheduledTaskCreator.CreateTaskOfMagicScheduledTask(taskName, user.DomainAndName, exePath);
        }

        /// <summary>
        /// 创建Center任务
        /// </summary>
        private static void TryCreateWindowsCenterTask(AccountItem user, List<string> toRemoveTasks)
        {
            var taskName = $"{CustomText.WindowsCenterApp}_{user.Name}";
            var exePath = $@"C:\Program Files (x86)\{CustomText.FamilyName}\{CustomText.WindowsCenterApp}\H3C.Entry.exe";
            if (!File.Exists(exePath))
            {
                LogService.Log.Error($"可执行文件{exePath}不存在");
                return;
            }
            if (toRemoveTasks.Contains(taskName))
            {
                toRemoveTasks.Remove(taskName);
                LogService.Log.Info($"任务{taskName}已存在,不重新创建");
                return;
            }
            ScheduledTaskCreator.CreateUserLoginTask(taskName, user.DomainAndName, exePath);
        }

        /// <summary>
        /// 创建日志计划任务
        /// </summary>
        private void TryCreateLogCollectorTask(AccountItem user, List<string> toRemoveTasks)
        {
            var taskName = $"{CustomText.H3CLogCollector}_{user.Name}";
            var exePath = $@"C:\Program Files (x86)\{CustomText.FamilyName}\{CustomText.H3CLogCollector}\H3C.Entry.exe";
            if (!File.Exists(exePath))
            {
                LogService.Log.Info($"可执行文件{exePath}不存在,跳过{taskName}创建");
                return;
            }
            if (toRemoveTasks.Contains(taskName))
            {
                toRemoveTasks.Remove(taskName);
                LogService.Log.Info($"任务{taskName}已存在,不重新创建");
                return;
            }
            ScheduledTaskCreator.CreateTaskOfH3CLogCollector(taskName, user.DomainAndName, exePath);
        }

        private static void SetUserAdmin(List<AccountItem> users)
        {
            foreach (var user in users)
            {
                if (user.IsAdmin)
                {
                    continue;
                }

                LogService.Log.Info($"帐户{user.Name}不是管理员,现在添加");
                AccountHelper.AddAccountAdmin(user.DomainAndName);
                var result = AccountHelper.GetUserIsAdmin(user.Name);
                LogService.Log.Info($"设置帐户{user.Name}为管理员结果:{result}");
                user.IsAdmin = result;
            }
        }

        /// <summary>
        /// 获取所有已有任务名称列表
        /// </summary>
        private List<string> GetRelatedTaskNames()
        {
            using TaskService service = new TaskService();
            using TaskFolder folder = service.RootFolder;
            return folder.GetTasks().Select(task => task.Name).
                    Where(name => name.StartsWith(CustomText.WindowsCenterApp) ||
                                  name.StartsWith(CustomText.ScheduledTaskApp) ||
                                  name.StartsWith(CustomText.H3CLogCollector)).ToList();
        }

        /// <summary>
        /// 删除任务
        /// </summary>
        private void DeleteTasks(IEnumerable<string> tasks)
        {
            using TaskService service = new TaskService();
            using TaskFolder folder = service.RootFolder;
            foreach (var task in tasks)
            {
                folder.DeleteTask(task);
            }
            LogService.Log.Info($"清理任务:{JsonConvert.SerializeObject(tasks)}");
        }
    }
}
using Microsoft.Win32.TaskScheduler;
using System;

namespace MagicScheduledTask
{
    internal class ScheduledTaskCreator
    {
        /// <summary>
        /// 创建计划任务工具(本程序)所用计划任务
        /// </summary>
        public static void CreateTaskOfMagicScheduledTask(string taskName, string userId, string exePath)
        {
            try
            {
                RegisterTask(taskName, userId, exePath,
                    definition =>
                    {
                        // 帐户添加时启动
                        definition.Triggers.Add(new EventTrigger()
                        {
                            Enabled = true,
                            Subscription =
                                "<QueryList><Query Id=\"0\" Path=\"Security\"><Select Path=\"Security\">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4722]]</Select></Query></QueryList>"
                        });

                        // 帐户移除时启动
                        definition.Triggers.Add(new EventTrigger()
                        {
                            Enabled = true,
                            Subscription =
                                "<QueryList><Query Id=\"0\" Path=\"Security\"><Select Path=\"Security\">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4726]]</Select></Query></QueryList>"
                        });
                    });
                LogService.Log.Info($"为帐户{userId}创建任务{taskName}成功");
            }
            catch (Exception ex)
            {
                LogService.Log.Error($"为帐户{userId}创建任务{taskName}失败:", ex);
            }
        }

        /// <summary>
        /// 为指定用户创建在该用户登录时启动的计划任务
        /// </summary>
        public static void CreateUserLoginTask(string taskName, string userId, string exePath)
        {
            try
            {
                RegisterTask(taskName, userId, exePath,
                    definition =>
                    {
                        // 用户登录时启动
                        definition.Triggers.Add(new LogonTrigger()
                        {
                            Enabled = true,
                            UserId = userId,
                        });

                        // 用户解锁时启动
                        definition.Triggers.Add(new SessionStateChangeTrigger()
                        {
                            Enabled = true,
                            StateChange = TaskSessionStateChangeType.SessionUnlock,
                            UserId = userId
                        });
                    });
                LogService.Log.Info($"为帐户{userId}创建任务{taskName}成功");
            }
            catch (Exception ex)
            {
                LogService.Log.Error($"为帐户{userId}创建任务{taskName}失败:", ex);
            }
        }
        /// <summary>
        /// 创建日志收集器所用计划任务
        /// </summary>
        public static void CreateTaskOfH3CLogCollector(string taskName, string userId, string exePath)
        {
            try
            {
                RegisterTask(taskName, userId, exePath,
                    definition =>
                    {
                        // 用户登录时启动
                        definition.Triggers.Add(new LogonTrigger()
                        {
                            Enabled = true,
                            UserId = userId,
                        });

                        // 用户解锁时启动
                        definition.Triggers.Add(new SessionStateChangeTrigger()
                        {
                            Enabled = true,
                            StateChange = TaskSessionStateChangeType.SessionUnlock,
                            UserId = userId
                        });

                        // 每天早上9点启动
                        definition.Triggers.Add(new DailyTrigger()
                        {
                            Enabled = true,
                            StartBoundary = DateTime.Today.AddHours(9),
                        });
                    });
                LogService.Log.Info($"为帐户{userId}创建任务{taskName}成功");
            }
            catch (Exception ex)
            {
                LogService.Log.Error($"为帐户{userId}创建任务{taskName}失败:", ex);
            }
        }

        /// <summary>
        /// 注册任务
        /// </summary>
        private static void RegisterTask(string taskName, string userId, string exePath, Action<TaskDefinition> taskDef)
        {
            using TaskService service = new TaskService();
            using TaskDefinition definition = service.NewTask();
            definition.RegistrationInfo.Version = new Version(1, 0, 0);
            definition.RegistrationInfo.Date = DateTime.Now;
            definition.RegistrationInfo.Author = $"{Environment.UserDomainName}\\{Environment.UserName}";

            definition.Actions.Add(new ExecAction(exePath));

            definition.Principal.RunLevel = TaskRunLevel.Highest;
            definition.Principal.UserId = userId;
            definition.Principal.LogonType = TaskLogonType.InteractiveToken;

            definition.Settings.DisallowStartIfOnBatteries = false; // 只有在交流电源下才执行
            definition.Settings.RunOnlyIfIdle = false; // 仅当计算机空闲下才执行
            definition.Settings.Enabled = true;
            definition.Settings.AllowDemandStart = true;
            definition.Settings.AllowHardTerminate = true;

            taskDef(definition);

            using var task = service.RootFolder.RegisterTaskDefinition(taskName, definition,
                TaskCreation.CreateOrUpdate,
                userId, null,
                TaskLogonType.None,
                "");
        }
    }
}
namespace MagicScheduledTask
{
    internal class ScheduledTaskFlag
    {
        /// <summary>
        /// 任务标识(前缀)
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 可执行文件路径
        /// </summary>
        public string ExePath { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace MagicScheduledTask
{
    public class AccountHelper
    {
        /// <summary>
        /// 获取帐户
        /// </summary>
        /// <returns></returns>
        public static List<AccountItem> GetAccounts()
        {
            int status = NetUserEnum(null, 1, FILTER_NORMAL_ACCOUNT, out IntPtr bufPtr, MAX_PREFERRED_LENGTH, out int countRead, out _, out _);
            if (status != NERR_Success)
            {
                LogService.Log.Error($"NetUserEnum获取用户失败,错误码: {status}");
                NetApiBufferFree(bufPtr);
                return new List<AccountItem>();
            }

            USER_INFO_1[] users = new USER_INFO_1[countRead];
            IntPtr iter = bufPtr; // 迭代指针
            for (int i = 0; i < countRead; i++)
            {
                users[i] = (USER_INFO_1)Marshal.PtrToStructure(iter, typeof(USER_INFO_1));
                iter = (IntPtr)((UInt64)iter + (UInt64)Marshal.SizeOf(typeof(USER_INFO_1)));
            }
            NetApiBufferFree(bufPtr);

            var list = new List<AccountItem>();
            for (int i = 0; i < countRead; ++i)
            {
                AccountItem item = new AccountItem();
                item.Name = users[i].usri1_name;
                item.IsAdmin = users[i].usri1_priv == USER_PRIV_ADMIN; // 是否为管理员
                item.IsDisabled = (users[i].usri1_flags & UF_ACCOUNTDISABLE) != 0; // 是否为禁用帐户
                item.DomainAndName = $"{Environment.UserDomainName}\\{item.Name}";
                list.Add(item);
            }

            // 添加是管理员的域帐户
            var domainAccounts = GetDomainAdminAccounts();
            foreach (var domainAccount in domainAccounts)
            {
                var i = list.FindIndex(x =>
                {
                    if (x.Name == domainAccount.Name)
                    {
                        x.DomainAndName = domainAccount.DomainAndName;
                        return true;
                    }
                    return false;
                });
                if (i < 0)
                {
                    // TODO 临时的区分域帐户方法:将含域帐户列表里有,但普通帐户列表没有的帐户视为域帐户
                    domainAccount.IsDomainUser = true;
                    list.Add(domainAccount);
                }
            }

            return list;
        }

        /// <summary>
        /// 直接判断一个帐户名当前是不是管理员
        /// </summary>
        public static bool GetUserIsAdmin(string username)
        {
            int status = NetUserGetInfo(null, username, 1, out IntPtr bufPtr);
            if (status != NERR_Success)
            {
                LogService.Log.Error($"NetUserGetInfo调用失败,错误码: {status}");
                NetApiBufferFree(bufPtr);
                return false;
            }
            USER_INFO_1 info = (USER_INFO_1)Marshal.PtrToStructure(bufPtr, typeof(USER_INFO_1));
            bool isAdmin = info.usri1_priv == USER_PRIV_ADMIN;
            NetApiBufferFree(bufPtr);
            return isAdmin;
        }

        /// <summary>
        /// 为帐户添加管理员权限(注意如果已经在Administrators组,再添加会报错)
        /// </summary>
        public static void AddAccountAdmin(string userDomainAndName)
        {
            LOCALGROUP_MEMBERS_INFO_3 info = new LOCALGROUP_MEMBERS_INFO_3();
            info.lgrmi3_domainandname = userDomainAndName;
            int result = NetLocalGroupAddMembers(null, "Administrators", 3, ref info, 1);
            if (result != NERR_Success)
            {
                LogService.Log.Error($"NetLocalGroupAddMembers为{userDomainAndName}添加管理员失败,错误码:{result}");
            }
        }

        /// <summary>
        /// 获取含域帐户的管理员帐户名列表
        /// </summary>
        private static List<AccountItem> GetDomainAdminAccounts()
        {
            int status = NetLocalGroupGetMembers(null, "Administrators", 2, out IntPtr bufPtr, MAX_PREFERRED_LENGTH,
                out int itemCount, out _, IntPtr.Zero);
            if (status != NERR_Success)
            {
                LogService.Log.Error($"NetLocalGroupGetMembers调用失败,错误码: {status}");
                NetApiBufferFree(bufPtr);
                return new List<AccountItem>();
            }
            LOCALGROUP_MEMBERS_INFO_2[] items = new LOCALGROUP_MEMBERS_INFO_2[itemCount];
            IntPtr itr = bufPtr; // 迭代指针
            for (int i = 0; i < itemCount; i++)
            {
                items[i] = (LOCALGROUP_MEMBERS_INFO_2)Marshal.PtrToStructure(itr, typeof(LOCALGROUP_MEMBERS_INFO_2));
                itr = (IntPtr)((UInt64)itr + (UInt64)Marshal.SizeOf(typeof(LOCALGROUP_MEMBERS_INFO_2)));
            }

            var result = items.Where(item => item.lgrmi2_sidusage == SidTypeUser /* 筛选类型是用户的SID */ )
                .Select(item =>
                {
                    AccountItem account = new AccountItem();
                    account.DomainAndName = item.lgrmi2_domainandname;
                    account.IsAdmin = true;
                    var charIndex = account.DomainAndName.LastIndexOf('\\');
                    if (charIndex < 0)
                    {
                        account.Name = account.DomainAndName;
                    }
                    else
                    {
                        account.Name = account.DomainAndName.Substring(charIndex + 1);
                    }
                    return account;
                })
                .ToList();
            NetApiBufferFree(bufPtr);
            return result;
        }

        /// <summary>
        /// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/ns-lmaccess-user_info_1
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct USER_INFO_1
        {
            [MarshalAs(UnmanagedType.LPWStr)]
            public string usri1_name;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string usri1_password;
            public int usri1_password_age;
            public int usri1_priv;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string usri1_home_dir;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string usri1_comment;
            public int usri1_flags;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string usri1_script_path;
        }

        /// <summary>
        /// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netuserenum
        /// </summary>
        [DllImport("Netapi32.dll")]
        private static extern int NetUserEnum(
            [MarshalAs(UnmanagedType.LPWStr)]
            string servername,
            int level,
            int filter,
            out IntPtr bufptr,
            int prefmaxlen,
            out int entriesread,
            out int totalentries,
            out int resume_handle);

        [DllImport("Netapi32.dll")]
        private static extern int NetApiBufferFree(IntPtr Buffer);

        /// <summary>
        /// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netusergetinfo
        /// </summary>
        [DllImport("Netapi32.dll")]
        private static extern int NetUserGetInfo(
            [MarshalAs(UnmanagedType.LPWStr)] string servername,
            [MarshalAs(UnmanagedType.LPWStr)] string username,
            int level,
            out IntPtr bufPtr);


        /// <summary>
        /// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/ns-lmaccess-localgroup_members_info_2
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct LOCALGROUP_MEMBERS_INFO_2
        {
            public IntPtr lgrmi2_sid;  // 成员安全标识符
            public int lgrmi2_sidusage; // 成员安全标识符使用情况
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lgrmi2_domainandname; // 成员名
        }

        /// <summary>
        /// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/ns-lmaccess-localgroup_members_info_3
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct LOCALGROUP_MEMBERS_INFO_3
        {
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lgrmi3_domainandname; // 成员名
        }

        [DllImport("Netapi32.dll")]
        private static extern int NetLocalGroupGetMembers(
            [MarshalAs(UnmanagedType.LPWStr)] string servername,
            [MarshalAs(UnmanagedType.LPWStr)] string localgroupname,
            int level,
            out IntPtr bufptr,
            int prefmaxlen,
            out int entriesread,
            out int totalentries,
            IntPtr resumeHandle);

        /// <summary>
        /// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers
        /// </summary>
        [DllImport("Netapi32.dll")]
        private static extern int NetLocalGroupAddMembers(
            [MarshalAs(UnmanagedType.LPWStr)] string servername,
            [MarshalAs(UnmanagedType.LPWStr)] string groupname,
            int level,
            ref LOCALGROUP_MEMBERS_INFO_3 bufptr,
            int totalentries);

        private const int NERR_Success = 0;
        private const int FILTER_NORMAL_ACCOUNT = 2;
        private const int MAX_PREFERRED_LENGTH = -1;
        private const int USER_PRIV_ADMIN = 2;
        private const int UF_ACCOUNTDISABLE = 2;
        private const int SidTypeUser = 1;
    }
}
namespace MagicScheduledTask
{
    public class AccountItem
    {
        /// <summary>
        /// 帐户名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 是否为管理员
        /// </summary>
        public bool IsAdmin { get; set; }

        /// <summary>
        /// 是否已禁用
        /// </summary>
        public bool IsDisabled { get; set; }

        /// <summary>
        /// 是否为域帐户
        /// </summary>
        public bool IsDomainUser { get; set; }

        /// <summary>
        /// 域和帐户名(用于添加管理员)
        /// </summary>
        public string DomainAndName { get; set; }
    }
}

 

posted on 2025-09-04 10:00  TanZhiWei  阅读(15)  评论(0)    收藏  举报