diff --git a/winPEAS/winPEASexe/Tests/Properties/AssemblyInfo.cs b/winPEAS/winPEASexe/Tests/Properties/AssemblyInfo.cs index 4f5cbeb06..21b6c03df 100644 --- a/winPEAS/winPEASexe/Tests/Properties/AssemblyInfo.cs +++ b/winPEAS/winPEASexe/Tests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/winPEAS/winPEASexe/Tests/SmokeTests.cs b/winPEAS/winPEASexe/Tests/SmokeTests.cs index e355180d4..fa71d042b 100644 --- a/winPEAS/winPEASexe/Tests/SmokeTests.cs +++ b/winPEAS/winPEASexe/Tests/SmokeTests.cs @@ -11,8 +11,8 @@ public void ShouldRunWinPeass() { try { - string[] args = new string[] { - "systeminfo", "servicesinfo", "processinfo", "applicationsinfo", "browserinfo", "debug" + string[] args = new string[] { + "systeminfo", "servicesinfo", "processinfo", "applicationsinfo", "browserinfo", "debug" }; Program.Main(args); } diff --git a/winPEAS/winPEASexe/winPEAS/Checks/ApplicationsInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/ApplicationsInfo.cs index 1d3e578df..7e75254b4 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/ApplicationsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/ApplicationsInfo.cs @@ -27,8 +27,8 @@ void PrintActiveWindow() { Beaprint.MainPrint("Current Active Window Application"); string title = ApplicationInfoHelper.GetActiveWindowTitle(); - List permsFile = PermissionsHelper.GetPermissionsFile(title, winPEAS.Checks.Checks.CurrentUserSiDs); - List permsFolder = PermissionsHelper.GetPermissionsFolder(title, winPEAS.Checks.Checks.CurrentUserSiDs); + List permsFile = PermissionsHelper.GetPermissionsFile(title, Checks.CurrentUserSiDs); + List permsFolder = PermissionsHelper.GetPermissionsFolder(title, Checks.CurrentUserSiDs); if (permsFile.Count > 0) { Beaprint.BadPrint(" " + title); @@ -188,8 +188,8 @@ void PrintScheduled() foreach (Dictionary sapp in scheduled_apps) { - List fileRights = PermissionsHelper.GetPermissionsFile(sapp["Action"], winPEAS.Checks.Checks.CurrentUserSiDs); - List dirRights = PermissionsHelper.GetPermissionsFolder(sapp["Action"], winPEAS.Checks.Checks.CurrentUserSiDs); + List fileRights = PermissionsHelper.GetPermissionsFile(sapp["Action"], Checks.CurrentUserSiDs); + List dirRights = PermissionsHelper.GetPermissionsFolder(sapp["Action"], Checks.CurrentUserSiDs); string formString = " ({0}) {1}: {2}"; if (fileRights.Count > 0) @@ -238,8 +238,8 @@ void PrintDeviceDrivers() foreach (var driver in DeviceDrivers.GetDeviceDriversNoMicrosoft()) { string pathDriver = driver.Key; - List fileRights = PermissionsHelper.GetPermissionsFile(pathDriver, winPEAS.Checks.Checks.CurrentUserSiDs); - List dirRights = PermissionsHelper.GetPermissionsFolder(pathDriver, winPEAS.Checks.Checks.CurrentUserSiDs); + List fileRights = PermissionsHelper.GetPermissionsFile(pathDriver, Checks.CurrentUserSiDs); + List dirRights = PermissionsHelper.GetPermissionsFolder(pathDriver, Checks.CurrentUserSiDs); Dictionary colorsD = new Dictionary() { diff --git a/winPEAS/winPEASexe/winPEAS/Checks/Checks.cs b/winPEAS/winPEASexe/winPEAS/Checks/Checks.cs index cf81d1676..cd5e6f3e3 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/Checks.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/Checks.cs @@ -169,7 +169,7 @@ internal static void Run(string[] args) { MaxRegexFileSize = Int32.Parse(parts[1]); } - + } if (string.Equals(arg, "-lolbas", StringComparison.CurrentCultureIgnoreCase)) @@ -363,8 +363,8 @@ private static void CreateDynamicLists() try { Beaprint.GrayPrint(" - Creating disabled users list..."); - Checks.PaintDisabledUsers = string.Join("|", User.GetMachineUsers(false, true, false, false, false)); - PaintDisabledUsersNoAdministrator = Checks.PaintDisabledUsers.Replace("|Administrator", "").Replace("Administrator|", "").Replace("Administrator", ""); + PaintDisabledUsers = string.Join("|", User.GetMachineUsers(false, true, false, false, false)); + PaintDisabledUsersNoAdministrator = PaintDisabledUsers.Replace("|Administrator", "").Replace("Administrator|", "").Replace("Administrator", ""); } catch (Exception ex) { @@ -411,7 +411,7 @@ private static void CheckRegANSI() try { if (RegistryHelper.GetRegValue("HKCU", "CONSOLE", "VirtualTerminalLevel") == "" && RegistryHelper.GetRegValue("HKCU", "CONSOLE", "VirtualTerminalLevel") == "") - System.Console.WriteLine(@"ANSI color bit for Windows is not set. If you are execcuting this from a Windows terminal inside the host you should run 'REG ADD HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1' and then start a new CMD"); + Console.WriteLine(@"ANSI color bit for Windows is not set. If you are executing this from a Windows terminal inside the host you should run 'REG ADD HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1' and then start a new CMD"); } catch (Exception ex) { @@ -425,7 +425,7 @@ private static void CheckLongPath() { if (RegistryHelper.GetRegValue("HKLM", @"SYSTEM\CurrentControlSet\Control\FileSystem", "LongPathsEnabled") != "1") { - System.Console.WriteLine(@"Long paths are disabled, so the maximum length of a path supported is 260chars (this may cause false negatives when looking for files). If you are admin, you can enable it with 'REG ADD HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v VirtualTerminalLevel /t REG_DWORD /d 1' and then start a new CMD"); + Console.WriteLine(@"Long paths are disabled, so the maximum length of a path supported is 260 chars (this may cause false negatives when looking for files). If you are admin, you can enable it with 'REG ADD HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v VirtualTerminalLevel /t REG_DWORD /d 1' and then start a new CMD"); IsLongPath = false; } else diff --git a/winPEAS/winPEASexe/winPEAS/Checks/EventsInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/EventsInfo.cs index 01c8946f0..04049eb1a 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/EventsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/EventsInfo.cs @@ -10,7 +10,7 @@ namespace winPEAS.Checks { internal class EventsInfo : ISystemCheck - { + { public void PrintInfo(bool isDebug) { Beaprint.GreatPrint("Interesting Events information"); @@ -23,7 +23,7 @@ public void PrintInfo(bool isDebug) PrintPowerShellEvents, PowerOnEvents, }.ForEach(action => CheckRunner.Run(action, isDebug)); - } + } private static void PrintPowerShellEvents() { @@ -91,7 +91,7 @@ private static void PrintLogonEvents() } var logonInfos = Logon.GetLogonInfos(lastDays); - + foreach (var info in logonInfos.LogonEventInfos) { Beaprint.BadPrint($" Subject User Name : {info.SubjectUserName}\n" + @@ -102,13 +102,13 @@ private static void PrintLogonEvents() $" Lm Package : {info.LmPackage}\n" + $" Logon Type : {info.LogonType}\n" + $" Target User Name : {info.TargetUserName}\n" + - $" Target Domain Name : {info.TargetDomainName}\n" + + $" Target Domain Name : {info.TargetDomainName}\n" + $" Target Outbound User Name : {info.TargetOutboundUserName}\n" + $" Target Outbound Domain Name : {info.TargetOutboundDomainName}\n"); Beaprint.PrintLineSeparator(); } - + if (logonInfos.NTLMv1LoggedUsersSet.Count > 0 || logonInfos.NTLMv2LoggedUsersSet.Count > 0) { Beaprint.BadPrint(" NTLM relay might be possible - other users authenticate to this machine using NTLM!"); @@ -151,7 +151,7 @@ private static void PrintExplicitLogonEvents() { var lastDays = 30; - Beaprint.MainPrint($"Printing Explicit Credential Events (4648) for last {lastDays} days - A process logged on using plaintext credentials\n"); + Beaprint.MainPrint($"Printing Explicit Credential Events (4648) for last {lastDays} days - A process logged on using plaintext credentials\n"); if (!MyUtils.IsHighIntegrity()) { diff --git a/winPEAS/winPEASexe/winPEAS/Checks/FileAnalysis.cs b/winPEAS/winPEASexe/winPEAS/Checks/FileAnalysis.cs index 02b41285e..d6ff565a4 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/FileAnalysis.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/FileAnalysis.cs @@ -27,7 +27,7 @@ public void PrintInfo(bool isDebug) }.ForEach(action => CheckRunner.Run(action, isDebug)); } - private static List InitializeFileSearch(bool useProgramFiles=true) + private static List InitializeFileSearch(bool useProgramFiles = true) { var files = new List(); var systemDrive = $"{SearchHelper.SystemDrive}\\"; @@ -101,7 +101,7 @@ private static bool[] Search(List files, string fileName, FileSe isFileFound = Regex.IsMatch(fold, pattern, RegexOptions.IgnoreCase); if (isFileFound) break; } - } + } } else { @@ -118,7 +118,8 @@ private static bool[] Search(List files, string fileName, FileSe if (isFileFound) { - if (!somethingFound) { + if (!somethingFound) + { Beaprint.MainPrint($"Found {searchName} Files"); somethingFound = true; } @@ -132,7 +133,7 @@ private static bool[] Search(List files, string fileName, FileSe } } // there are inner sections - else + else { foreach (var innerFileToSearch in fileSettings.files) { @@ -143,7 +144,7 @@ private static bool[] Search(List files, string fileName, FileSe } } - + return new bool[] { false, somethingFound }; } @@ -177,7 +178,7 @@ private static List SearchContent(string text, string regex_str, bool ca } return foundMatches; } - + if (!is_re_match) { return foundMatches; @@ -187,10 +188,10 @@ private static List SearchContent(string text, string regex_str, bool ca foreach (Match match in rgx.Matches(text)) { if (cont > 10) break; - + if (match.Value.Length < 400 && match.Value.Trim().Length > 2) foundMatches.Add(match.Value); - + cont++; } } @@ -348,12 +349,12 @@ private static void PrintYAMLRegexesSearchFiles() { timer.Start(); } - + try { - string text = System.IO.File.ReadAllText(f.FullPath); - + string text = File.ReadAllText(f.FullPath); + results = SearchContent(text, regex.regex, (bool)regex.caseinsensitive); if (results.Count > 0) { @@ -429,7 +430,7 @@ private static string GetRegexpFromString(string str) // . -> \. // * -> .* // add $ at the end to avoid false positives - + var pattern = str.Replace(".", @"\.") .Replace("*", @".*"); @@ -447,11 +448,11 @@ private static string GetRegexpFromString(string str) resultsCount++; if (resultsCount > ListFileLimit) return false; - + // If contains undesireable string, stop processing if (fileSettings.remove_path != null && fileSettings.remove_path.Length > 0) { - foreach(var rem_path in fileSettings.remove_path.Split('|')) + foreach (var rem_path in fileSettings.remove_path.Split('|')) { if (fileInfo.FullPath.ToLower().Contains(rem_path.ToLower())) return false; @@ -460,19 +461,23 @@ private static string GetRegexpFromString(string str) if (fileSettings.type == "f") { - var colors = new Dictionary(); - colors.Add(fileInfo.Filename, Beaprint.ansi_color_bad); + var colors = new Dictionary + { + { fileInfo.Filename, Beaprint.ansi_color_bad } + }; Beaprint.AnsiPrint($"File: {fileInfo.FullPath}", colors); - if (!(bool)fileSettings.just_list_file) + if (!(bool)fileSettings.just_list_file) { GrepResult(fileInfo, fileSettings); } } else if (fileSettings.type == "d") { - var colors = new Dictionary(); - colors.Add(fileInfo.Filename, Beaprint.ansi_color_bad); + var colors = new Dictionary + { + { fileInfo.Filename, Beaprint.ansi_color_bad } + }; Beaprint.AnsiPrint($"Folder: {fileInfo.FullPath}", colors); // just list the directory @@ -487,7 +492,7 @@ private static string GetRegexpFromString(string str) } else { - // should not happen + // should not happen } } @@ -531,11 +536,11 @@ private static void GrepResult(CustomFileInfo fileInfo, FileSettings fileSetting { lineGrep = SanitizeLineGrep(fileSettings.line_grep); } - + fileContent = fileContent.Where(line => (!string.IsNullOrWhiteSpace(fileSettings.good_regex) && Regex.IsMatch(line, fileSettings.good_regex, RegexOptions.IgnoreCase)) || (!string.IsNullOrWhiteSpace(fileSettings.bad_regex) && Regex.IsMatch(line, fileSettings.bad_regex, RegexOptions.IgnoreCase)) || (!string.IsNullOrWhiteSpace(lineGrep) && Regex.IsMatch(line, lineGrep, RegexOptions.IgnoreCase))); - } + } var content = string.Join(Environment.NewLine, fileContent); diff --git a/winPEAS/winPEASexe/winPEAS/Checks/FilesInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/FilesInfo.cs index 532453f8c..251393632 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/FilesInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/FilesInfo.cs @@ -21,7 +21,7 @@ namespace winPEAS.Checks internal class FilesInfo : ISystemCheck { static readonly string _patternsFileCredsColor = @"RDCMan.settings|.rdg|_history|httpd.conf|.htpasswd|.gitconfig|.git-credentials|Dockerfile|docker-compose.ymlaccess_tokens.db|accessTokens.json|azureProfile.json|appcmd.exe|scclient.exe|unattend.txt|access.log|error.log|credential|password|.gpg|.pgp|config.php|elasticsearch|kibana.|.p12|\.der|.csr|.crt|.cer|.pem|known_hosts|id_rsa|id_dsa|.ovpn|tomcat-users.xml|web.config|.kdbx|.key|KeePass.config|ntds.dir|Ntds.dit|sam|system|SAM|SYSTEM|security|software|SECURITY|SOFTWARE|FreeSSHDservice.ini|sysprep.inf|sysprep.xml|unattend.xml|unattended.xml|vnc|groups.xml|services.xml|scheduledtasks.xml|printers.xml|drives.xml|datasources.xml|php.ini|https.conf|https-xampp.conf|my.ini|my.cnf|access.log|error.log|server.xml|setupinfo|pagefile.sys|NetSetup.log|iis6.log|AppEvent.Evt|SecEvent.Evt|default.sav|security.sav|software.sav|system.sav|ntuser.dat|index.dat|bash.exe|wsl.exe"; - // static readonly string _patternsFileCreds = @"RDCMan.settings;*.rdg;*_history*;httpd.conf;.htpasswd;.gitconfig;.git-credentials;Dockerfile;docker-compose.yml;access_tokens.db;accessTokens.json;azureProfile.json;appcmd.exe;scclient.exe;*.gpg$;*.pgp$;*config*.php;elasticsearch.y*ml;kibana.y*ml;*.p12$;*.cer$;known_hosts;*id_rsa*;*id_dsa*;*.ovpn;tomcat-users.xml;web.config;*.kdbx;KeePass.config;Ntds.dit;SAM;SYSTEM;security;software;FreeSSHDservice.ini;sysprep.inf;sysprep.xml;*vnc*.ini;*vnc*.c*nf*;*vnc*.txt;*vnc*.xml;php.ini;https.conf;https-xampp.conf;my.ini;my.cnf;access.log;error.log;server.xml;ConsoleHost_history.txt;pagefile.sys;NetSetup.log;iis6.log;AppEvent.Evt;SecEvent.Evt;default.sav;security.sav;software.sav;system.sav;ntuser.dat;index.dat;bash.exe;wsl.exe;unattend.txt;*.der$;*.csr$;unattend.xml;unattended.xml;groups.xml;services.xml;scheduledtasks.xml;printers.xml;drives.xml;datasources.xml;setupinfo;setupinfo.bak"; + // static readonly string _patternsFileCreds = @"RDCMan.settings;*.rdg;*_history*;httpd.conf;.htpasswd;.gitconfig;.git-credentials;Dockerfile;docker-compose.yml;access_tokens.db;accessTokens.json;azureProfile.json;appcmd.exe;scclient.exe;*.gpg$;*.pgp$;*config*.php;elasticsearch.y*ml;kibana.y*ml;*.p12$;*.cer$;known_hosts;*id_rsa*;*id_dsa*;*.ovpn;tomcat-users.xml;web.config;*.kdbx;KeePass.config;Ntds.dit;SAM;SYSTEM;security;software;FreeSSHDservice.ini;sysprep.inf;sysprep.xml;*vnc*.ini;*vnc*.c*nf*;*vnc*.txt;*vnc*.xml;php.ini;https.conf;https-xampp.conf;my.ini;my.cnf;access.log;error.log;server.xml;ConsoleHost_history.txt;pagefile.sys;NetSetup.log;iis6.log;AppEvent.Evt;SecEvent.Evt;default.sav;security.sav;software.sav;system.sav;ntuser.dat;index.dat;bash.exe;wsl.exe;unattend.txt;*.der$;*.csr$;unattend.xml;unattended.xml;groups.xml;services.xml;scheduledtasks.xml;printers.xml;drives.xml;datasources.xml;setupinfo;setupinfo.bak"; private static readonly IList patternsFileCreds = new List() { @@ -159,7 +159,7 @@ void PrintCloudCreds() { string formString = " {0} ({1})\n Accessed:{2} -- Size:{3}"; Beaprint.BadPrint(string.Format(formString, cc["file"], cc["Description"], cc["Accessed"], cc["Size"])); - System.Console.WriteLine(""); + Console.WriteLine(""); } } else @@ -182,7 +182,7 @@ void PrintUnattendFiles() { List pwds = Unattended.ExtractUnattendedPwd(path); Beaprint.BadPrint(" " + path); - System.Console.WriteLine(string.Join("\n", pwds)); + Console.WriteLine(string.Join("\n", pwds)); } } catch (Exception ex) @@ -233,11 +233,11 @@ private static void PrintMcAffeSitelistFiles() foreach (var site in sitelistFilesInfo.Sites) { Beaprint.NoColorPrint($" Share Name : {site.ShareName}"); - PrintColored( $" User Name : {site.UserName}", !string.IsNullOrWhiteSpace(site.UserName)); - PrintColored( $" Server : {site.Server}", !string.IsNullOrWhiteSpace(site.Server)); - PrintColored( $" Encrypted Password : {site.EncPassword}", !string.IsNullOrWhiteSpace(site.EncPassword)); - PrintColored( $" Decrypted Password : {site.DecPassword}", !string.IsNullOrWhiteSpace(site.DecPassword)); - Beaprint.NoColorPrint( $" Domain Name : {site.DomainName}\n" + + PrintColored($" User Name : {site.UserName}", !string.IsNullOrWhiteSpace(site.UserName)); + PrintColored($" Server : {site.Server}", !string.IsNullOrWhiteSpace(site.Server)); + PrintColored($" Encrypted Password : {site.EncPassword}", !string.IsNullOrWhiteSpace(site.EncPassword)); + PrintColored($" Decrypted Password : {site.DecPassword}", !string.IsNullOrWhiteSpace(site.DecPassword)); + Beaprint.NoColorPrint($" Domain Name : {site.DomainName}\n" + $" Name : {site.Name}\n" + $" Type : {site.Type}\n" + $" Relative Path : {site.RelativePath}\n"); @@ -291,7 +291,7 @@ void PrintWSLDistributions() const string rootDirectory = "Root directory"; const string runWith = "Run command"; - var colors = new Dictionary(); + var colors = new Dictionary(); new List { linpeas, @@ -410,7 +410,7 @@ void PrintUserCredsFiles() { try { - string pattern_color = "[cC][rR][eE][dD][eE][nN][tT][iI][aA][lL]|[pP][aA][sS][sS][wW][oO][rR][dD]"; + string pattern_color = "[cC][rR][eE][dD][eE][nN][tT][iI][aA][lL]|[pP][aA][sS][sS][wW][oO][rR][dD]"; var validExtensions = new HashSet { ".cnf", @@ -431,7 +431,7 @@ void PrintUserCredsFiles() }; Beaprint.MainPrint("Looking for possible password files in users homes"); - Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#credentials-inside-files"); + Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#credentials-inside-files"); var fileInfos = SearchHelper.SearchUserCredsFiles(); foreach (var fileInfo in fileInfos) @@ -463,7 +463,7 @@ void PrintRecycleBin() { //string pattern_bin = _patternsFileCreds + ";*password*;*credential*"; string pattern_bin = string.Join(";", patternsFileCreds) + ";*password*;*credential*"; - + Dictionary colorF = new Dictionary() { { _patternsFileCredsColor + "|.*password.*|.*credential.*", Beaprint.ansi_color_bad }, @@ -472,7 +472,7 @@ void PrintRecycleBin() Beaprint.MainPrint("Looking inside the Recycle Bin for creds files"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#credentials-inside-files"); List> recy_files = InterestingFiles.InterestingFiles.GetRecycleBin(); - + foreach (Dictionary rec_file in recy_files) { foreach (string pattern in pattern_bin.Split(';')) @@ -480,7 +480,7 @@ void PrintRecycleBin() if (Regex.Match(rec_file["Name"], pattern.Replace("*", ".*"), RegexOptions.IgnoreCase).Success) { Beaprint.DictPrint(rec_file, colorF, true); - System.Console.WriteLine(); + Console.WriteLine(); } } } @@ -507,7 +507,7 @@ void PrintUsersInterestingFiles() Beaprint.MainPrint("Searching known files that can contain creds in home"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#credentials-inside-files"); - + var files = SearchHelper.SearchUsersInterestingFiles(); Beaprint.AnsiPrint(" " + string.Join("\n ", files), colorF); @@ -567,7 +567,7 @@ void PrintOtherUsersInterestingFiles() try { Beaprint.MainPrint("Searching interesting files in other users home directories (can be slow)\n"); - + // check if admin already, if yes, print a message, if not, try to enumerate all files if (MyUtils.IsHighIntegrity()) { @@ -751,7 +751,7 @@ private void PrintExecutablesInNonDefaultFoldersWithWritePermissions() ".cmd" }; - var files = SearchHelper.GetFilesFast(systemDrive, "*", excludedDirs); + var files = SearchHelper.GetFilesFast(systemDrive, "*", excludedDirs); foreach (var file in files) { @@ -825,14 +825,14 @@ private static void PrintMachineAndUserCertificateFiles() foreach (var certificateInfo in certificateInfos) { - + Beaprint.NoColorPrint($" Issuer : {certificateInfo.Issuer}\n" + $" Subject : {certificateInfo.Subject}\n" + - $" ValidDate : {certificateInfo.ValidDate}\n" + + $" ValidDate : {certificateInfo.ValidDate}\n" + $" ExpiryDate : {certificateInfo.ExpiryDate}\n" + - $" HasPrivateKey : {certificateInfo.HasPrivateKey}\n" + - $" StoreLocation : {certificateInfo.StoreLocation}\n" + - $" KeyExportable : {certificateInfo.KeyExportable}\n" + + $" HasPrivateKey : {certificateInfo.HasPrivateKey}\n" + + $" StoreLocation : {certificateInfo.StoreLocation}\n" + + $" KeyExportable : {certificateInfo.KeyExportable}\n" + $" Thumbprint : {certificateInfo.Thumbprint}\n"); if (!string.IsNullOrEmpty(certificateInfo.Template)) @@ -885,7 +885,7 @@ private static void PrintOutlookDownloads() } catch (Exception e) { - } + } } } catch (Exception ex) @@ -1033,7 +1033,7 @@ private static void PrintLOLBAS() //@"c:\windows.old", rootUsersSearchPath, documentsAndSettings - }; + }; var files = SearchHelper.GetFilesFast(systemDrive, "*", excludedDirs); diff --git a/winPEAS/winPEASexe/winPEAS/Checks/NetworkInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/NetworkInfo.cs index 83eb4b732..97a10e33b 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/NetworkInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/NetworkInfo.cs @@ -26,8 +26,8 @@ internal class NetworkInfo : ISystemCheck public void PrintInfo(bool isDebug) { - Beaprint.GreatPrint("Network Information"); - + Beaprint.GreatPrint("Network Information"); + new List { PrintNetShares, @@ -81,7 +81,7 @@ private void PrintHostsFile() { if (line.Length > 0 && line[0] != '#') { - System.Console.WriteLine(" " + line.Replace("\t", " ")); + Console.WriteLine(" " + line.Replace("\t", " ")); } } } @@ -304,8 +304,8 @@ private void PrintFirewallRules() Beaprint.GrayPrint(" DENY rules:"); foreach (Dictionary rule in Firewall.GetFirewallRules()) { - string filePerms = string.Join(", ", PermissionsHelper.GetPermissionsFile(rule["AppName"], winPEAS.Checks.Checks.CurrentUserSiDs)); - string folderPerms = string.Join(", ", PermissionsHelper.GetPermissionsFolder(rule["AppName"], winPEAS.Checks.Checks.CurrentUserSiDs)); + string filePerms = string.Join(", ", PermissionsHelper.GetPermissionsFile(rule["AppName"], Checks.CurrentUserSiDs)); + string folderPerms = string.Join(", ", PermissionsHelper.GetPermissionsFolder(rule["AppName"], Checks.CurrentUserSiDs)); string formString = " ({0}){1}[{2}]: {3} {4} {5} from {6} --> {7}"; if (filePerms.Length > 0) formString += "\n File Permissions: {8}"; @@ -389,8 +389,8 @@ private static void PrintInternetSettings() var info = InternetSettings.GetInternetSettingsInfo(); Beaprint.ColorPrint(" General Settings", Beaprint.LBLUE); - Beaprint.NoColorPrint($" {"Hive",-10} {"Key",-40} {"Value"}"); - + Beaprint.NoColorPrint($" {"Hive",-10} {"Key",-40} {"Value"}"); + foreach (var i in info.GeneralSettings) { Beaprint.NoColorPrint($" {i.Hive,-10} {i.ValueName,-40} {i.Value}"); @@ -410,9 +410,9 @@ private static void PrintInternetSettings() { Beaprint.NoColorPrint($" {i.Hive,-10} {i.ValueName,-40} {i.Interpretation}"); } - } - - Beaprint.ColorPrint("\n Zone Auth Settings", Beaprint.LBLUE); + } + + Beaprint.ColorPrint("\n Zone Auth Settings", Beaprint.LBLUE); if (info.ZoneAuthSettings.Count == 0) { Beaprint.NoColorPrint(" No Zone Auth Settings"); @@ -423,7 +423,7 @@ private static void PrintInternetSettings() { Beaprint.NoColorPrint($" {i.Interpretation}"); } - } + } } catch (Exception ex) { diff --git a/winPEAS/winPEASexe/winPEAS/Checks/ProcessInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/ProcessInfo.cs index 07a27dc98..e6e578958 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/ProcessInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/ProcessInfo.cs @@ -10,7 +10,7 @@ internal class ProcessInfo : ISystemCheck { public void PrintInfo(bool isDebug) { - Beaprint.GreatPrint("Processes Information"); + Beaprint.GreatPrint("Processes Information"); new List { @@ -101,7 +101,7 @@ void PrintVulnLeakedHandlers() Beaprint.DictPrint(vulnHandlers, colors, true); } - + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Checks/ServicesInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/ServicesInfo.cs index 7a33cd7dc..ddfdf4763 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/ServicesInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/ServicesInfo.cs @@ -20,7 +20,7 @@ public void PrintInfo(bool isDebug) { CheckRunner.Run(() => { - modifiableServices = ServicesInfoHelper.GetModifiableServices(winPEAS.Checks.Checks.CurrentUserSiDs); + modifiableServices = ServicesInfoHelper.GetModifiableServices(Checks.CurrentUserSiDs); }, isDebug); } catch (Exception ex) @@ -53,12 +53,12 @@ void PrintInterestingServices() foreach (Dictionary serviceInfo in services_info) { - List fileRights = PermissionsHelper.GetPermissionsFile(serviceInfo["FilteredPath"], winPEAS.Checks.Checks.CurrentUserSiDs); + List fileRights = PermissionsHelper.GetPermissionsFile(serviceInfo["FilteredPath"], Checks.CurrentUserSiDs); List dirRights = new List(); if (serviceInfo["FilteredPath"] != null && serviceInfo["FilteredPath"] != "") { - dirRights = PermissionsHelper.GetPermissionsFolder(Path.GetDirectoryName(serviceInfo["FilteredPath"]), winPEAS.Checks.Checks.CurrentUserSiDs); + dirRights = PermissionsHelper.GetPermissionsFolder(Path.GetDirectoryName(serviceInfo["FilteredPath"]), Checks.CurrentUserSiDs); } bool noQuotesAndSpace = MyUtils.CheckQuoteAndSpace(serviceInfo["PathName"]); @@ -159,7 +159,7 @@ void PrintWritableRegServices() { Beaprint.MainPrint("Looking if you can modify any service registry"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#services-registry-permissions", "Check if you can modify the registry of a service"); - List> regPerms = ServicesInfoHelper.GetWriteServiceRegs(winPEAS.Checks.Checks.CurrentUserSiDs); + List> regPerms = ServicesInfoHelper.GetWriteServiceRegs(Checks.CurrentUserSiDs); Dictionary colorsWR = new Dictionary() { diff --git a/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs index d4eee5df9..310071fd1 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/SystemInfo.cs @@ -5,21 +5,21 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using winPEAS._3rdParty.Watson; using winPEAS.Helpers; using winPEAS.Helpers.AppLocker; -using winPEAS._3rdParty.Watson; -using winPEAS.Info.SystemInfo.Printers; -using winPEAS.Info.SystemInfo.NamedPipes; -using winPEAS.Info.SystemInfo; -using winPEAS.Info.SystemInfo.SysMon; using winPEAS.Helpers.Extensions; using winPEAS.Helpers.Registry; +using winPEAS.Info.SystemInfo; using winPEAS.Info.SystemInfo.AuditPolicies; using winPEAS.Info.SystemInfo.DotNet; using winPEAS.Info.SystemInfo.GroupPolicy; -using winPEAS.Info.SystemInfo.WindowsDefender; -using winPEAS.Info.SystemInfo.PowerShell; +using winPEAS.Info.SystemInfo.NamedPipes; using winPEAS.Info.SystemInfo.Ntlm; +using winPEAS.Info.SystemInfo.PowerShell; +using winPEAS.Info.SystemInfo.Printers; +using winPEAS.Info.SystemInfo.SysMon; +using winPEAS.Info.SystemInfo.WindowsDefender; using winPEAS.Native.Enums; namespace winPEAS.Checks @@ -47,13 +47,13 @@ class SystemInfo : ISystemCheck { "3b576869-a4ec-4529-8536-b80a7769e899" , "Block Office applications from creating executable content "}, { "75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84" , "Block Office applications from injecting code into other processes"}, { "d3e037e1-3eb8-44c8-a917-57927947596d" , "Block JavaScript or VBScript from launching downloaded executable content"}, - { "be9ba2d9-53ea-4cdc-84e5-9b1eeee46550" , "Block executable content from email client and webmail"}, + { "be9ba2d9-53ea-4cdc-84e5-9b1eeee46550" , "Block executable content from email client and webmail"}, }; public void PrintInfo(bool isDebug) { Beaprint.GreatPrint("System Information"); - + new List { PrintBasicSystemInfo, @@ -107,7 +107,7 @@ private static void PrintBasicSystemInfo() { Globals.StrTrue, Beaprint.ansi_color_bad }, }; Beaprint.DictPrint(basicDictSystem, colorsSI, false); - System.Console.WriteLine(); + Console.WriteLine(); Watson.FindVulns(); //To update Watson, update the CVEs and add the new ones and update the main function so it uses new CVEs (becausfull with the Beaprints inside the FindVulns function) @@ -200,7 +200,7 @@ static void PrintTranscriptPS() Beaprint.MainPrint("PS default transcripts history"); Beaprint.InfoPrint("Read the PS history inside these files (if any)"); string drive = Path.GetPathRoot(Environment.SystemDirectory); - string transcriptsPath = drive + @"transcripts\"; + string transcriptsPath = drive + @"transcripts\"; string usersPath = $"{drive}users"; var users = Directory.EnumerateDirectories(usersPath, "*", SearchOption.TopDirectoryOnly); @@ -210,7 +210,7 @@ static void PrintTranscriptPS() { { "^.*", Beaprint.ansi_color_bad }, }; - + var results = new List(); var dict = new Dictionary() @@ -218,7 +218,7 @@ static void PrintTranscriptPS() // check \\transcripts\ folder {transcriptsPath, "*"}, }; - + foreach (var user in users) { // check the users directories @@ -290,12 +290,12 @@ private static void PrintAuditPoliciesInfo() Beaprint.NoColorPrint($" Domain : {policy.Domain}\n" + $" GPO : {policy.GPO}\n" + $" Type : {policy.Type}\n"); - + foreach (var entry in policy.Settings) { Beaprint.NoColorPrint($" {entry.Subcategory,50} : {entry.AuditType}"); } - + Beaprint.PrintLineSeparator(); } } @@ -366,15 +366,15 @@ static void PrintCredentialGuard() Beaprint.MainPrint("Credentials Guard"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/stealing-credentials/credentials-protections#credential-guard", "If enabled, a driver is needed to read LSASS memory"); string lsaCfgFlags = RegistryHelper.GetRegValue("HKLM", @"System\CurrentControlSet\Control\LSA", "LsaCfgFlags"); - + if (lsaCfgFlags == "1") { - System.Console.WriteLine(" Please, note that this only checks the LsaCfgFlags key value. This is not enough to enable Credentials Guard (but it's a strong indicator)."); + Console.WriteLine(" Please, note that this only checks the LsaCfgFlags key value. This is not enough to enable Credentials Guard (but it's a strong indicator)."); Beaprint.GoodPrint(" CredentialGuard is active with UEFI lock"); } else if (lsaCfgFlags == "2") { - System.Console.WriteLine(" Please, note that this only checks the LsaCfgFlags key value. This is not enough to enable Credentials Guard (but it's a strong indicator)."); + Console.WriteLine(" Please, note that this only checks the LsaCfgFlags key value. This is not enough to enable Credentials Guard (but it's a strong indicator)."); Beaprint.GoodPrint(" CredentialGuard is active without UEFI lock"); } else @@ -572,7 +572,7 @@ static void PrintWSUS() else if (using_HKLM_WSUS == "0") Beaprint.GoodPrint(" But UseWUServer is equals to 0, so it is not vulnerable!"); else - System.Console.WriteLine(" But UseWUServer is equals to " + using_HKLM_WSUS + ", so it may work or not"); + Console.WriteLine(" But UseWUServer is equals to " + using_HKLM_WSUS + ", so it may work or not"); } else { @@ -643,9 +643,9 @@ static void PrintAlwaysInstallElevated() string path = "Software\\Policies\\Microsoft\\Windows\\Installer"; string HKLM_AIE = RegistryHelper.GetRegValue("HKLM", path, "AlwaysInstallElevated"); string HKCU_AIE = RegistryHelper.GetRegValue("HKCU", path, "AlwaysInstallElevated"); - + if (HKLM_AIE == "1") - { + { Beaprint.BadPrint(" AlwaysInstallElevated set to 1 in HKLM!"); } @@ -672,7 +672,7 @@ private static void PrintNtlmSettings() try { var info = Ntlm.GetNtlmSettingsInfo(); - + string lmCompatibilityLevelColor = info.LanmanCompatibilityLevel >= 3 ? Beaprint.ansi_color_good : Beaprint.ansi_color_bad; Beaprint.ColorPrint($" LanmanCompatibilityLevel : {info.LanmanCompatibilityLevel} ({info.LanmanCompatibilityLevelString})\n", lmCompatibilityLevelColor); @@ -683,12 +683,12 @@ private static void PrintNtlmSettings() { "No signing", Beaprint.ansi_color_bad}, { "null", Beaprint.ansi_color_bad}, { "Require Signing", Beaprint.ansi_color_good}, - { "Negotiate signing", Beaprint.ansi_color_yellow}, + { "Negotiate signing", Beaprint.ansi_color_yellow}, { "Unknown", Beaprint.ansi_color_bad}, }; Beaprint.ColorPrint("\n NTLM Signing Settings", Beaprint.LBLUE); - Beaprint.AnsiPrint($" ClientRequireSigning : {info.ClientRequireSigning}\n" + + Beaprint.AnsiPrint($" ClientRequireSigning : {info.ClientRequireSigning}\n" + $" ClientNegotiateSigning : {info.ClientNegotiateSigning}\n" + $" ServerRequireSigning : {info.ServerRequireSigning}\n" + $" ServerNegotiateSigning : {info.ServerNegotiateSigning}\n" + @@ -727,13 +727,13 @@ private static void PrintNtlmSettings() } } - var ntlmOutboundRestrictionsColor = info.OutboundRestrictions == 2 ? Beaprint.ansi_color_good : Beaprint.ansi_color_bad; + var ntlmOutboundRestrictionsColor = info.OutboundRestrictions == 2 ? Beaprint.ansi_color_good : Beaprint.ansi_color_bad; Beaprint.ColorPrint("\n NTLM Auditing and Restrictions", Beaprint.LBLUE); Beaprint.NoColorPrint($" InboundRestrictions : {info.InboundRestrictions} ({info.InboundRestrictionsString})"); Beaprint.ColorPrint($" OutboundRestrictions : {info.OutboundRestrictions} ({info.OutboundRestrictionsString})", ntlmOutboundRestrictionsColor); Beaprint.NoColorPrint($" InboundAuditing : {info.InboundAuditing} ({info.InboundRestrictionsString})"); - Beaprint.NoColorPrint($" OutboundExceptions : {info.OutboundExceptions}"); + Beaprint.NoColorPrint($" OutboundExceptions : {info.OutboundExceptions}"); } catch (Exception ex) { @@ -783,7 +783,7 @@ private static void PrintNamedPipes() Beaprint.AnsiPrint(string.Format(formatString, namedPipe.Name, namedPipe.CurrentUserPerms, namedPipe.Sddl), colors); } } - catch (Exception ex) + catch (Exception ex) { //Beaprint.PrintException(ex.Message); } @@ -816,8 +816,8 @@ private void PrintSysmon() { PrintSysmonConfiguration(); PrintSysmonEventLogs(); - } - + } + private void PrintSysmonConfiguration() { Beaprint.MainPrint("Enumerating Sysmon configuration"); @@ -1070,7 +1070,7 @@ private static void PrintLSAInfo() } else if (kvp.Value.GetType().IsArray && (kvp.Value.GetType().GetElementType().ToString() == "System.Byte")) { - val = System.BitConverter.ToString((byte[])kvp.Value); + val = BitConverter.ToString((byte[])kvp.Value); } else { @@ -1086,12 +1086,12 @@ private static void PrintLSAInfo() Beaprint.BadPrint(" [!] WDigest is enabled - plaintext password extraction is possible!"); } - if (key.Equals("RunAsPPL", System.StringComparison.InvariantCultureIgnoreCase) && val == "1") + if (key.Equals("RunAsPPL", StringComparison.InvariantCultureIgnoreCase) && val == "1") { Beaprint.BadPrint(" [!] LSASS Protected Mode is enabled! You will not be able to access lsass.exe's memory easily."); } - if (key.Equals("DisableRestrictedAdmin", System.StringComparison.InvariantCultureIgnoreCase) && val == "0") + if (key.Equals("DisableRestrictedAdmin", StringComparison.InvariantCultureIgnoreCase) && val == "0") { Beaprint.BadPrint(" [!] RDP Restricted Admin Mode is enabled! You can use pass-the-hash to access RDP on this system."); } @@ -1107,7 +1107,7 @@ private static void PrintLocalGroupPolicy() { try { - Beaprint.MainPrint("Display Local Group Policy settings - local users/machine" ); + Beaprint.MainPrint("Display Local Group Policy settings - local users/machine"); var infos = GroupPolicy.GetLocalGroupPolicyInfos(); diff --git a/winPEAS/winPEASexe/winPEAS/Checks/UserInfo.cs b/winPEAS/winPEASexe/winPEAS/Checks/UserInfo.cs index ed0e1789e..e8a2fa245 100644 --- a/winPEAS/winPEASexe/winPEAS/Checks/UserInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Checks/UserInfo.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Runtime.InteropServices; -using System.Security.Cryptography; using System.Security.Principal; using winPEAS.Helpers; using winPEAS.Helpers.Extensions; @@ -39,7 +37,7 @@ internal class UserInfo : ISystemCheck public void PrintInfo(bool isDebug) { Beaprint.GreatPrint("Users Information"); - + new List { PrintCU, @@ -158,7 +156,7 @@ void PrintRdpSessions() try { Beaprint.MainPrint("RDP Sessions"); - List> rdp_sessions = Info.UserInfo.UserInfoHelper.GetRDPSessions(); + List> rdp_sessions = UserInfoHelper.GetRDPSessions(); if (rdp_sessions.Count > 0) { string format = " {0,-10}{1,-15}{2,-15}{3,-25}{4,-10}{5}"; @@ -263,7 +261,7 @@ void PrintPasswordPolicies() { Beaprint.MainPrint("Password Policies"); Beaprint.LinkPrint("", "Check for a possible brute-force"); - List> PPy = Info.UserInfo.UserInfoHelper.GetPasswordPolicy(); + List> PPy = UserInfoHelper.GetPasswordPolicy(); Beaprint.DictPrint(PPy, ColorsU(), false); } catch (Exception ex) @@ -282,7 +280,7 @@ private void PrintLogonSessions() foreach (var logonSession in logonSessions) { - Beaprint.NoColorPrint ($" Method: {logonSession.Method}\n" + + Beaprint.NoColorPrint($" Method: {logonSession.Method}\n" + $" Logon Server: {logonSession.LogonServer}\n" + $" Logon Server Dns Domain: {logonSession.LogonServerDnsDomain}\n" + $" Logon Id: {logonSession.LogonId}\n" + @@ -317,7 +315,7 @@ private static void PrintCurrentUserIdleTime() if (User32.GetLastInputInfo(ref lastInputInfo)) { var currentUser = WindowsIdentity.GetCurrent().Name; - var idleTimeMiliSeconds = (uint) Environment.TickCount - lastInputInfo.Time; + var idleTimeMiliSeconds = (uint)Environment.TickCount - lastInputInfo.Time; var timeSpan = TimeSpan.FromMilliseconds(idleTimeMiliSeconds); var idleTimeString = $"{timeSpan.Hours:D2}h:{timeSpan.Minutes:D2}m:{timeSpan.Seconds:D2}s:{timeSpan.Milliseconds:D3}ms"; @@ -364,7 +362,7 @@ private static void PrintLocalUsers() lastLogon = lastLogon.AddSeconds(localUser.last_logon).ToLocalTime(); } - Beaprint.AnsiPrint( $" Computer Name : {computerName}\n" + + Beaprint.AnsiPrint($" Computer Name : {computerName}\n" + $" User Name : {localUser.name}\n" + $" User Id : {localUser.user_id}\n" + $" Is Enabled : {enabled}\n" + diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/AppLockerHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/AppLockerHelper.cs index 67d6d18c8..a300f2850 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/AppLockerHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/AppLockerHelper.cs @@ -7,9 +7,9 @@ namespace winPEAS.Helpers.AppLocker { internal static class AppLockerHelper - { + { private static readonly HashSet _appLockerByPassDirectoriesSet = new HashSet - { + { @"C:\Windows\Temp", @"C:\Windows\System32\spool\drivers\color", @"C:\Windows\Tasks", @@ -88,7 +88,7 @@ public static void PrintAppLockerPolicy() PrintFilePathRules(rule); PrintFilePublisherRules(rule); } - } + } } catch (COMException) { @@ -116,7 +116,7 @@ private static void PrintFilePublisherRules(AppLockerPolicyRuleCollection rule) var color = GetColorBySid(filePublisherRule.UserOrGroupSid); - Beaprint.ColorPrint( $" User Or Group Sid: {filePublisherRule.UserOrGroupSid}\n", color); + Beaprint.ColorPrint($" User Or Group Sid: {filePublisherRule.UserOrGroupSid}\n", color); Beaprint.GoodPrint($" Conditions"); @@ -150,10 +150,10 @@ private static void PrintFilePathRules(AppLockerPolicyRuleCollection rule) $" Translated Name: {normalizedName}\n" + $" Description: {filePathRule.Description}\n" + $" Action: {filePathRule.Action}"); - + var color = GetColorBySid(filePathRule.UserOrGroupSid); - Beaprint.ColorPrint( $" User Or Group Sid: {filePathRule.UserOrGroupSid}\n", color); + Beaprint.ColorPrint($" User Or Group Sid: {filePathRule.UserOrGroupSid}\n", color); Beaprint.GoodPrint($" Conditions"); @@ -241,7 +241,7 @@ private static void PrintFilePathRules(AppLockerPolicyRuleCollection rule) Beaprint.ColorPrint($" No potential bypass found while recursively checking files/subfolders " + $"for write or equivalent permissions with depth: {FolderCheckMaxDepth}\n" + $" Check permissions manually.", Beaprint.YELLOW); - } + } } } } @@ -328,39 +328,42 @@ private static bool CheckFilesAndSubfolders(string path, string ruleType, int de try { - var subfolders = Directory.EnumerateDirectories(path); - var files = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); + if (Directory.Exists(path)) + { + var subfolders = Directory.EnumerateDirectories(path); + var files = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); - ruleType = ruleType.ToLower(); + ruleType = ruleType.ToLower(); - if (!_appLockerFileExtensionsByType.ContainsKey(ruleType)) - { - throw new ArgumentException(nameof(ruleType)); - } - - var filteredFiles = - (from file in files - let extension = Path.GetExtension(file)?.ToLower() ?? string.Empty - where _appLockerFileExtensionsByType[ruleType].Contains(extension) - select file).ToList(); - - // first check write access for files - if (filteredFiles.Any(CheckFileWriteAccess)) - { - return true; - } + if (!_appLockerFileExtensionsByType.ContainsKey(ruleType)) + { + throw new ArgumentException(nameof(ruleType)); + } - // if we have not found any writable file, - // check subfolders for write access - if (subfolders.Any(subfolder => CheckDirectoryWriteAccess(subfolder, out bool _, isGoodPrint: false))) - { - return true; - } + var filteredFiles = + (from file in files + let extension = Path.GetExtension(file)?.ToLower() ?? string.Empty + where _appLockerFileExtensionsByType[ruleType].Contains(extension) + select file).ToList(); - // check recursively all the subfolders for files/sub-subfolders - if (subfolders.Any(subfolder => CheckFilesAndSubfolders(subfolder, ruleType, depth + 1))) - { - return true; + // first check write access for files + if (filteredFiles.Any(CheckFileWriteAccess)) + { + return true; + } + + // if we have not found any writable file, + // check subfolders for write access + if (subfolders.Any(subfolder => CheckDirectoryWriteAccess(subfolder, out bool _, isGoodPrint: false))) + { + return true; + } + + // check recursively all the subfolders for files/sub-subfolders + if (subfolders.Any(subfolder => CheckFilesAndSubfolders(subfolder, ruleType, depth + 1))) + { + return true; + } } } catch (Exception) diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/IAppIdPolicyHandler.cs b/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/IAppIdPolicyHandler.cs index ad7bbcc82..17ea5e30c 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/IAppIdPolicyHandler.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/AppLocker/IAppIdPolicyHandler.cs @@ -5,79 +5,79 @@ namespace winPEAS.Helpers.AppLocker { [Guid("B6FEA19E-32DD-4367-B5B7-2F5DA140E87D")] - [TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)] - [ComImport] - public interface IAppIdPolicyHandler - { - // Token: 0x06000001 RID: 1 - [DispId(1)] - [MethodImpl(MethodImplOptions.InternalCall)] - void SetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath, [MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy); + [TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)] + [ComImport] + public interface IAppIdPolicyHandler + { + // Token: 0x06000001 RID: 1 + [DispId(1)] + [MethodImpl(MethodImplOptions.InternalCall)] + void SetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath, [MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy); - // Token: 0x06000002 RID: 2 - [DispId(2)] - [MethodImpl(MethodImplOptions.InternalCall)] - [return: MarshalAs(UnmanagedType.BStr)] - string GetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath); + // Token: 0x06000002 RID: 2 + [DispId(2)] + [MethodImpl(MethodImplOptions.InternalCall)] + [return: MarshalAs(UnmanagedType.BStr)] + string GetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath); - // Token: 0x06000003 RID: 3 - [DispId(3)] - [MethodImpl(MethodImplOptions.InternalCall)] - [return: MarshalAs(UnmanagedType.BStr)] - string GetEffectivePolicy(); + // Token: 0x06000003 RID: 3 + [DispId(3)] + [MethodImpl(MethodImplOptions.InternalCall)] + [return: MarshalAs(UnmanagedType.BStr)] + string GetEffectivePolicy(); - // Token: 0x06000004 RID: 4 - [DispId(4)] - [MethodImpl(MethodImplOptions.InternalCall)] - int IsFileAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrFilePath, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); + // Token: 0x06000004 RID: 4 + [DispId(4)] + [MethodImpl(MethodImplOptions.InternalCall)] + int IsFileAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrFilePath, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); - // Token: 0x06000005 RID: 5 - [DispId(5)] - [MethodImpl(MethodImplOptions.InternalCall)] - int IsPackageAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrPublisherName, [MarshalAs(UnmanagedType.BStr)][In] string bstrPackageName, [In] ulong ullPackageVersion, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); - } + // Token: 0x06000005 RID: 5 + [DispId(5)] + [MethodImpl(MethodImplOptions.InternalCall)] + int IsPackageAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrPublisherName, [MarshalAs(UnmanagedType.BStr)][In] string bstrPackageName, [In] ulong ullPackageVersion, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); + } - // Token: 0x02000003 RID: 3 - [CoClass(typeof(AppIdPolicyHandlerClass))] - [Guid("B6FEA19E-32DD-4367-B5B7-2F5DA140E87D")] - [ComImport] - public interface AppIdPolicyHandler : IAppIdPolicyHandler - { - } + // Token: 0x02000003 RID: 3 + [CoClass(typeof(AppIdPolicyHandlerClass))] + [Guid("B6FEA19E-32DD-4367-B5B7-2F5DA140E87D")] + [ComImport] + public interface AppIdPolicyHandler : IAppIdPolicyHandler + { + } - // Token: 0x02000004 RID: 4 - [Guid("F1ED7D4C-F863-4DE6-A1CA-7253EFDEE1F3")] - [ClassInterface(ClassInterfaceType.None)] - [TypeLibType(TypeLibTypeFlags.FCanCreate)] - [ComImport] - public class AppIdPolicyHandlerClass : IAppIdPolicyHandler, AppIdPolicyHandler - { + // Token: 0x02000004 RID: 4 + [Guid("F1ED7D4C-F863-4DE6-A1CA-7253EFDEE1F3")] + [ClassInterface(ClassInterfaceType.None)] + [TypeLibType(TypeLibTypeFlags.FCanCreate)] + [ComImport] + public class AppIdPolicyHandlerClass : IAppIdPolicyHandler, AppIdPolicyHandler + { - // Token: 0x06000007 RID: 7 - [DispId(1)] - [MethodImpl(MethodImplOptions.InternalCall)] - public virtual extern void SetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath, [MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy); + // Token: 0x06000007 RID: 7 + [DispId(1)] + [MethodImpl(MethodImplOptions.InternalCall)] + public virtual extern void SetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath, [MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy); - // Token: 0x06000008 RID: 8 - [DispId(2)] - [MethodImpl(MethodImplOptions.InternalCall)] - [return: MarshalAs(UnmanagedType.BStr)] - public virtual extern string GetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath); + // Token: 0x06000008 RID: 8 + [DispId(2)] + [MethodImpl(MethodImplOptions.InternalCall)] + [return: MarshalAs(UnmanagedType.BStr)] + public virtual extern string GetPolicy([MarshalAs(UnmanagedType.BStr)][In] string bstrLdapPath); - // Token: 0x06000009 RID: 9 - [DispId(3)] - [MethodImpl(MethodImplOptions.InternalCall)] - [return: MarshalAs(UnmanagedType.BStr)] - public virtual extern string GetEffectivePolicy(); + // Token: 0x06000009 RID: 9 + [DispId(3)] + [MethodImpl(MethodImplOptions.InternalCall)] + [return: MarshalAs(UnmanagedType.BStr)] + public virtual extern string GetEffectivePolicy(); - // Token: 0x0600000A RID: 10 - [DispId(4)] - [MethodImpl(MethodImplOptions.InternalCall)] - public virtual extern int IsFileAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrFilePath, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); + // Token: 0x0600000A RID: 10 + [DispId(4)] + [MethodImpl(MethodImplOptions.InternalCall)] + public virtual extern int IsFileAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrFilePath, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); - // Token: 0x0600000B RID: 11 - [DispId(5)] - [MethodImpl(MethodImplOptions.InternalCall)] - public virtual extern int IsPackageAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrPublisherName, [MarshalAs(UnmanagedType.BStr)][In] string bstrPackageName, [In] ulong ullPackageVersion, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); - } + // Token: 0x0600000B RID: 11 + [DispId(5)] + [MethodImpl(MethodImplOptions.InternalCall)] + public virtual extern int IsPackageAllowed([MarshalAs(UnmanagedType.BStr)][In] string bstrXmlPolicy, [MarshalAs(UnmanagedType.BStr)][In] string bstrPublisherName, [MarshalAs(UnmanagedType.BStr)][In] string bstrPackageName, [In] ulong ullPackageVersion, [MarshalAs(UnmanagedType.BStr)][In] string bstrUserSid, out Guid pguidResponsibleRuleId); + } } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/Beaprint.cs b/winPEAS/winPEASexe/winPEAS/Helpers/Beaprint.cs index c49e84863..a0c5da9b8 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/Beaprint.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/Beaprint.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using System.Threading; namespace winPEAS.Helpers { @@ -105,7 +104,7 @@ public static void PrintInit() PrintLegend(); Console.WriteLine(); - Console.WriteLine(BLUE + " You can find a Windows local PE Checklist here: "+YELLOW+"https://book.hacktricks.xyz/windows-hardening/checklist-windows-privilege-escalation"); + Console.WriteLine(BLUE + " You can find a Windows local PE Checklist here: " + YELLOW + "https://book.hacktricks.xyz/windows-hardening/checklist-windows-privilege-escalation"); } static void PrintLegend() @@ -142,7 +141,7 @@ public static void PrintUsage() Console.WriteLine(LCYAN + " debug" + GRAY + " Display debugging information - memory usage, method execution time" + NOCOLOR); Console.WriteLine(LCYAN + " log[=logfile]" + GRAY + $" Log all output to file defined as logfile, or to \"{Checks.Checks.DefaultLogFile}\" if not specified" + NOCOLOR); Console.WriteLine(LCYAN + " max-regex-file-size=1000000" + GRAY + $" Max file size (in Bytes) to search regex in. Default: {Checks.Checks.MaxRegexFileSize}B" + NOCOLOR); - + Console.WriteLine(); Console.WriteLine(GREEN + " Additional checks (slower):"); Console.WriteLine(LCYAN + " -lolbas" + GRAY + $" Run additional LOLBAS check" + NOCOLOR); diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/Credential.cs b/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/Credential.cs index e41facd1b..d3e14afc0 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/Credential.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/Credential.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; -using System.Text; using winPEAS.Native; using winPEAS.Native.Enums; @@ -394,6 +393,6 @@ internal void LoadInternal(NativeMethods.CREDENTIAL credential) PersistenceType = (PersistenceType)credential.Persist; Description = credential.Comment; LastWriteTimeUtc = DateTime.FromFileTimeUtc(credential.LastWritten); - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/CredentialType.cs b/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/CredentialType.cs index d32113e61..6fe778a5f 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/CredentialType.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/CredentialType.cs @@ -1,4 +1,4 @@ namespace winPEAS.Helpers.CredentialManager { - + } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/NativeMethods.cs b/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/NativeMethods.cs index 246a899a0..ae9d461c5 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/NativeMethods.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/CredentialManager/NativeMethods.cs @@ -1,9 +1,9 @@ -using System; +using Microsoft.Win32.SafeHandles; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; using winPEAS.Native; namespace winPEAS.Helpers.CredentialManager @@ -18,7 +18,7 @@ namespace winPEAS.Helpers.CredentialManager /// public class NativeMethods { - + /// /// The CREDENTIAL structure contains an individual credential. /// diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/DomainHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/DomainHelper.cs index f293a240f..59c240a5e 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/DomainHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/DomainHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; using winPEAS.Native; using winPEAS.Native.Enums; @@ -15,9 +14,9 @@ internal static class DomainHelper { internal class Win32 { - public const int ErrorSuccess = 0; + public const int ErrorSuccess = 0; + - } public static string IsDomainJoined() diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/HandlesHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/HandlesHelper.cs index 143555dac..062b916cd 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/HandlesHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/HandlesHelper.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; using System.Security.Principal; using System.Text; -using System.Threading.Tasks; namespace winPEAS.Helpers { @@ -244,7 +242,7 @@ public static VULNERABLE_HANDLER_INFO checkExploitaible(SYSTEM_HANDLE_TABLE_ENTR { string perm = PermissionsHelper.PermInt2Str((int)h.GrantedAccess, PermissionType.WRITEABLE_OR_EQUIVALENT); - if (perm != null && perm.Length> 0) + if (perm != null && perm.Length > 0) { vulnHandler.isVuln = true; vulnHandler.reason = perm; @@ -438,9 +436,11 @@ public static List GetAllHandlers() // Get the owner of a process given the PID public static Dictionary GetProcU(Process p) { - Dictionary data = new Dictionary(); - data["name"] = ""; - data["sid"] = ""; + Dictionary data = new Dictionary + { + ["name"] = "", + ["sid"] = "" + }; IntPtr pHandle = IntPtr.Zero; try { @@ -471,7 +471,7 @@ public static PT_RELEVANT_INFO getProcInfoById(int pid) PT_RELEVANT_INFO pri = new PT_RELEVANT_INFO(); Process proc = Process.GetProcessById(pid); - Dictionary user = GetProcU(proc); + Dictionary user = GetProcU(proc); StringBuilder fileName = new StringBuilder(2000); Native.Psapi.GetProcessImageFileName(proc.Handle, fileName, 2000); @@ -586,7 +586,7 @@ public static KEY_RELEVANT_INFO getKeyHandlerInfo(IntPtr handle) { // This shouldn't be needed if (path.StartsWith("\\")) path = path.Substring(1); - hive = Helpers.Registry.RegistryHelper.CheckIfExists(path); + hive = Registry.RegistryHelper.CheckIfExists(path); } if (path.StartsWith("\\")) diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/MemoryHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/MemoryHelper.cs index 1e37dda06..18588fdc7 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/MemoryHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/MemoryHelper.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; namespace winPEAS.Helpers { diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/MyUtils.cs b/winPEAS/winPEASexe/winPEAS/Helpers/MyUtils.cs index 13474fa9c..a905c08cf 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/MyUtils.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/MyUtils.cs @@ -76,7 +76,7 @@ public static string GetExecutableFromPath(string path) } //Check if rundll32 - string[] binaryPathdll32 = binaryPath.Split(new string[] {"Rundll32.exe"}, StringSplitOptions.None); + string[] binaryPathdll32 = binaryPath.Split(new string[] { "Rundll32.exe" }, StringSplitOptions.None); if (binaryPathdll32.Length > 1) { @@ -224,7 +224,7 @@ public static string ExecCMD(string args, string alternative_binary = "") return strOutput; } - private static string[] suffixes = new[] {" B", " KB", " MB", " GB", " TB", " PB"}; + private static string[] suffixes = new[] { " B", " KB", " MB", " GB", " TB", " PB" }; public static string ConvertBytesToHumanReadable(double number, int precision = 2) { diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/PermissionsHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/PermissionsHelper.cs index 0c070ad8b..929af6653 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/PermissionsHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/PermissionsHelper.cs @@ -1,11 +1,11 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; using System.Text.RegularExpressions; -using Microsoft.Win32; namespace winPEAS.Helpers { @@ -354,14 +354,17 @@ public static string GetFolderFromString(string path) results[path] = String.Join(", ", GetPermissionsFolder(path, Checks.Checks.CurrentUserSiDs)); if (string.IsNullOrEmpty(results[path])) { - foreach (string d in Directory.EnumerateDirectories(path)) + if (Directory.Exists(path)) { - foreach (string f in Directory.EnumerateFiles(d)) + foreach (string d in Directory.EnumerateDirectories(path)) { - results[f] = String.Join(", ", GetPermissionsFile(f, Checks.Checks.CurrentUserSiDs)); + foreach (string f in Directory.EnumerateFiles(d)) + { + results[f] = String.Join(", ", GetPermissionsFile(f, Checks.Checks.CurrentUserSiDs)); + } + cont += 1; + results.Concat(GetRecursivePrivs(d, cont)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } - cont += 1; - results.Concat(GetRecursivePrivs(d, cont)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } } } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/ProgressBar.cs b/winPEAS/winPEASexe/winPEAS/Helpers/ProgressBar.cs index 81e05e6e2..941138049 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/ProgressBar.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/ProgressBar.cs @@ -4,85 +4,85 @@ namespace winPEAS.Helpers { - internal class ProgressBar : IDisposable, IProgress - { - private const int blockCount = 10; - private readonly TimeSpan animationInterval = TimeSpan.FromSeconds(1.0 / 8); - private const string animation = @"|/-\"; + internal class ProgressBar : IDisposable, IProgress + { + private const int blockCount = 10; + private readonly TimeSpan animationInterval = TimeSpan.FromSeconds(1.0 / 8); + private const string animation = @"|/-\"; - private readonly Timer timer; + private readonly Timer timer; - private double currentProgress = 0; - private string currentText = string.Empty; - private bool disposed = false; - private int animationIndex = 0; + private double currentProgress = 0; + private string currentText = string.Empty; + private bool disposed = false; + private int animationIndex = 0; - public ProgressBar() - { - timer = new Timer(TimerHandler, new object(), animationInterval, animationInterval); - } + public ProgressBar() + { + timer = new Timer(TimerHandler, new object(), animationInterval, animationInterval); + } - public void Report(double value) - { - // Make sure value is in [0..1] range - value = Math.Max(0, Math.Min(1, value)); - Interlocked.Exchange(ref currentProgress, value); - } + public void Report(double value) + { + // Make sure value is in [0..1] range + value = Math.Max(0, Math.Min(1, value)); + Interlocked.Exchange(ref currentProgress, value); + } - private void TimerHandler(object state) - { - lock (timer) - { - if (disposed) return; + private void TimerHandler(object state) + { + lock (timer) + { + if (disposed) return; - int progressBlockCount = (int)(currentProgress * blockCount); - int percent = (int)(currentProgress * 100); - string text = string.Format("[{0}{1}] {2,3}% {3}", - new string('#', progressBlockCount), new string('-', blockCount - progressBlockCount), - percent, - animation[animationIndex++ % animation.Length]); - UpdateText(text); - } - } + int progressBlockCount = (int)(currentProgress * blockCount); + int percent = (int)(currentProgress * 100); + string text = string.Format("[{0}{1}] {2,3}% {3}", + new string('#', progressBlockCount), new string('-', blockCount - progressBlockCount), + percent, + animation[animationIndex++ % animation.Length]); + UpdateText(text); + } + } - private void UpdateText(string text) - { - // Get length of common portion - int commonPrefixLength = 0; - int commonLength = Math.Min(currentText.Length, text.Length); - while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) - { - commonPrefixLength++; - } + private void UpdateText(string text) + { + // Get length of common portion + int commonPrefixLength = 0; + int commonLength = Math.Min(currentText.Length, text.Length); + while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) + { + commonPrefixLength++; + } - // Backtrack to the first differing character - StringBuilder outputBuilder = new StringBuilder(); - outputBuilder.Append('\b', currentText.Length - commonPrefixLength); + // Backtrack to the first differing character + StringBuilder outputBuilder = new StringBuilder(); + outputBuilder.Append('\b', currentText.Length - commonPrefixLength); - // Output new suffix - outputBuilder.Append(text.Substring(commonPrefixLength)); + // Output new suffix + outputBuilder.Append(text.Substring(commonPrefixLength)); - // If the new text is shorter than the old one: delete overlapping characters - int overlapCount = currentText.Length - text.Length; - if (overlapCount > 0) - { - outputBuilder.Append(' ', overlapCount); - outputBuilder.Append('\b', overlapCount); - } + // If the new text is shorter than the old one: delete overlapping characters + int overlapCount = currentText.Length - text.Length; + if (overlapCount > 0) + { + outputBuilder.Append(' ', overlapCount); + outputBuilder.Append('\b', overlapCount); + } - Console.Write(outputBuilder); - currentText = text; - } + Console.Write(outputBuilder); + currentText = text; + } - public void Dispose() - { - lock (timer) - { - disposed = true; - UpdateText(string.Empty); - timer.Dispose(); - } - } + public void Dispose() + { + lock (timer) + { + disposed = true; + UpdateText(string.Empty); + timer.Dispose(); + } + } - } + } } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/Registry/RegistryHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/Registry/RegistryHelper.cs index b86987a18..51f0d15f3 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/Registry/RegistryHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/Registry/RegistryHelper.cs @@ -1,7 +1,7 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Win32; namespace winPEAS.Helpers.Registry { @@ -177,7 +177,7 @@ public static string[] GetUserSIDs() internal static uint? GetDwordValue(string hive, string key, string val) { - string strValue = RegistryHelper.GetRegValue(hive, key, val); + string strValue = GetRegValue(hive, key, val); if (uint.TryParse(strValue, out uint res)) { diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/Search/Patterns.cs b/winPEAS/winPEASexe/winPEAS/Helpers/Search/Patterns.cs index d9a1f2c64..87bffba56 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/Search/Patterns.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/Search/Patterns.cs @@ -3,7 +3,7 @@ namespace winPEAS.Helpers.Search { static class Patterns - { + { public static readonly HashSet WhitelistExtensions = new HashSet() { ".cer", @@ -11,7 +11,7 @@ static class Patterns ".der", ".p12", }; - + public static readonly HashSet WhiteListExactfilenamesWithExtensions = new HashSet() { "docker-compose.yml", @@ -21,6 +21,6 @@ static class Patterns public static readonly IList WhiteListRegexp = new List() { "config.*\\.php$", - }; + }; } } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/Search/SearchHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/Search/SearchHelper.cs index 3e1c52d8a..2cb81e1cc 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/Search/SearchHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/Search/SearchHelper.cs @@ -92,13 +92,13 @@ public static List GetFilesFast(string folder, string pattern = Beaprint.LongPathWarning(f.FullName); } } - ) ; + ); }); }); return files.ToList(); - } - + } + private static List GetFiles(string folder, string pattern = "*") { DirectoryInfo dirInfo; @@ -221,43 +221,43 @@ internal static void CreateSearchDirectoriesList() { // c:\users string rootUsersSearchPath = $"{SystemDrive}\\Users\\"; - SearchHelper.RootDirUsers = SearchHelper.GetFilesFast(rootUsersSearchPath, GlobalPattern, isFoldersIncluded: true); + RootDirUsers = GetFilesFast(rootUsersSearchPath, GlobalPattern, isFoldersIncluded: true); // c:\users\current_user string rootCurrentUserSearchPath = Environment.GetEnvironmentVariable("USERPROFILE"); - SearchHelper.RootDirCurrentUser = SearchHelper.GetFilesFast(rootCurrentUserSearchPath, GlobalPattern, isFoldersIncluded: true); + RootDirCurrentUser = GetFilesFast(rootCurrentUserSearchPath, GlobalPattern, isFoldersIncluded: true); // c:\Program Files\ string rootProgramFiles = $"{SystemDrive}\\Program Files\\"; - SearchHelper.ProgramFiles = SearchHelper.GetFilesFast(rootProgramFiles, GlobalPattern, isFoldersIncluded: true); + ProgramFiles = GetFilesFast(rootProgramFiles, GlobalPattern, isFoldersIncluded: true); // c:\Program Files (x86)\ string rootProgramFilesX86 = $"{SystemDrive}\\Program Files (x86)\\"; - SearchHelper.ProgramFilesX86 = SearchHelper.GetFilesFast(rootProgramFilesX86, GlobalPattern, isFoldersIncluded: true); + ProgramFilesX86 = GetFilesFast(rootProgramFilesX86, GlobalPattern, isFoldersIncluded: true); // c:\Documents and Settings\ string documentsAndSettings = $"{SystemDrive}\\Documents and Settings\\"; - SearchHelper.DocumentsAndSettings = SearchHelper.GetFilesFast(documentsAndSettings, GlobalPattern, isFoldersIncluded: true); + DocumentsAndSettings = GetFilesFast(documentsAndSettings, GlobalPattern, isFoldersIncluded: true); // c:\ProgramData\Microsoft\Group Policy\History string groupPolicyHistory = $"{SystemDrive}\\ProgramData\\Microsoft\\Group Policy\\History"; - SearchHelper.GroupPolicyHistory = SearchHelper.GetFilesFast(groupPolicyHistory, GlobalPattern, isFoldersIncluded: true); + GroupPolicyHistory = GetFilesFast(groupPolicyHistory, GlobalPattern, isFoldersIncluded: true); // c:\Documents and Settings\All Users\Application Data\\Microsoft\\Group Policy\\History string groupPolicyHistoryLegacy = $"{documentsAndSettings}\\All Users\\Application Data\\Microsoft\\Group Policy\\History"; //SearchHelper.GroupPolicyHistoryLegacy = SearchHelper.GetFilesFast(groupPolicyHistoryLegacy, globalPattern); - var groupPolicyHistoryLegacyFiles = SearchHelper.GetFilesFast(groupPolicyHistoryLegacy, GlobalPattern, isFoldersIncluded: true); - SearchHelper.GroupPolicyHistory.AddRange(groupPolicyHistoryLegacyFiles); + var groupPolicyHistoryLegacyFiles = GetFilesFast(groupPolicyHistoryLegacy, GlobalPattern, isFoldersIncluded: true); + GroupPolicyHistory.AddRange(groupPolicyHistoryLegacyFiles); } internal static void CleanLists() { - SearchHelper.RootDirUsers = null; - SearchHelper.RootDirCurrentUser = null; - SearchHelper.ProgramFiles = null; - SearchHelper.ProgramFilesX86 = null; - SearchHelper.DocumentsAndSettings = null; - SearchHelper.GroupPolicyHistory = null; + RootDirUsers = null; + RootDirCurrentUser = null; + ProgramFiles = null; + ProgramFilesX86 = null; + DocumentsAndSettings = null; + GroupPolicyHistory = null; GC.Collect(); } @@ -270,10 +270,10 @@ internal static IEnumerable SearchUserCredsFiles() ".*password.*" }; - foreach (var file in SearchHelper.RootDirUsers) - { - //string extLower = file.Extension.ToLower(); - + foreach (var file in RootDirUsers) + { + //string extLower = file.Extension.ToLower(); + if (!file.IsDirectory) { string nameLower = file.Filename.ToLower(); @@ -297,7 +297,7 @@ internal static List SearchUsersInterestingFiles() { var result = new List(); - foreach (var file in SearchHelper.RootDirCurrentUser) + foreach (var file in RootDirCurrentUser) { if (!file.IsDirectory) { @@ -322,7 +322,7 @@ internal static List SearchUsersInterestingFiles() } } - } + } } return result; @@ -337,7 +337,7 @@ internal static List FindCachedGPPPassword() ".xml" }; - foreach (var file in SearchHelper.GroupPolicyHistory) + foreach (var file in GroupPolicyHistory) { if (!file.IsDirectory) { @@ -361,14 +361,14 @@ internal static IEnumerable SearchMcAfeeSitelistFiles() }; string programDataPath = $"{SystemDrive}\\ProgramData\\"; - var programData = SearchHelper.GetFilesFast(programDataPath, GlobalPattern); + var programData = GetFilesFast(programDataPath, GlobalPattern); var searchFiles = new List(); - searchFiles.AddRange(SearchHelper.ProgramFiles); - searchFiles.AddRange(SearchHelper.ProgramFilesX86); + searchFiles.AddRange(ProgramFiles); + searchFiles.AddRange(ProgramFilesX86); searchFiles.AddRange(programData); - searchFiles.AddRange(SearchHelper.DocumentsAndSettings); - searchFiles.AddRange(SearchHelper.RootDirUsers); + searchFiles.AddRange(DocumentsAndSettings); + searchFiles.AddRange(RootDirUsers); foreach (var file in searchFiles) { @@ -403,7 +403,7 @@ internal static List SearchCurrentUserDocs() ".pdf", }; - foreach (var file in SearchHelper.RootDirCurrentUser) + foreach (var file in RootDirCurrentUser) { if (!file.IsDirectory) { @@ -426,7 +426,7 @@ internal static List SearchCurrentUserDocs() } } } - } + } } return result; @@ -451,7 +451,7 @@ internal static List SearchUsersDocs() ".pdf", }; - foreach (var file in SearchHelper.RootDirUsers) + foreach (var file in RootDirUsers) { if (!file.IsDirectory) { @@ -474,7 +474,7 @@ internal static List SearchUsersDocs() } } } - } + } } return result; diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfig.cs b/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfig.cs index ab1a824ef..9fc209aa3 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfig.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfig.cs @@ -8,12 +8,13 @@ public class RegularExpressions { public string name { get; set; } public RegularExpression[] regexes { get; set; } - public class RegularExpression { + public class RegularExpression + { public string name { get; set; } public string regex { get; set; } public bool caseinsensitive { get; set; } - + public string disable { get; set; } } } @@ -25,65 +26,65 @@ public class YamlConfig public class FileParam { - public string name { get; set; } - public FileSettings value { get; set; } - } + public string name { get; set; } + public FileSettings value { get; set; } + } public class SearchParameters - { + { public class FileSettings - { - public string bad_regex { get; set; } + { + public string bad_regex { get; set; } // public string check_extra_path { get; set; } // not used in Winpeas - public string good_regex { get; set; } - public bool? just_list_file { get; set; } - public string line_grep { get; set; } - public bool? only_bad_lines { get; set; } - public bool? remove_empty_lines { get; set; } + public string good_regex { get; set; } + public bool? just_list_file { get; set; } + public string line_grep { get; set; } + public bool? only_bad_lines { get; set; } + public bool? remove_empty_lines { get; set; } // public string remove_path { get; set; } // not used in Winpeas - public string remove_regex { get; set; } + public string remove_regex { get; set; } public string remove_path { get; set; } // public string[] search_in { get; set; } // not used in Winpeas - public string type { get; set; } - public FileParam[] files { get; set; } + public string type { get; set; } + public FileParam[] files { get; set; } } public class FileParameters { - public string file { get; set; } - public FileSettings options { get; set; } + public string file { get; set; } + public FileSettings options { get; set; } } public class Config { - public bool auto_check { get; set; } + public bool auto_check { get; set; } } - public Config config { get; set; } - public string[] disable { get; set; } // disabled scripts - linpeas/winpeas - public FileParam[] files { get; set; } + public Config config { get; set; } + public string[] disable { get; set; } // disabled scripts - linpeas/winpeas + public FileParam[] files { get; set; } } public class SearchParams { - public string name { get; set; } - public SearchParameters value { get; set; } + public string name { get; set; } + public SearchParameters value { get; set; } } public class Defaults { - public bool auto_check { get; set; } - public string bad_regex { get; set; } + public bool auto_check { get; set; } + public string bad_regex { get; set; } //public string check_extra_path { get; set; } not used in winpeas - public string good_regex { get; set; } - public bool just_list_file { get; set; } - public string line_grep { get; set; } - public bool only_bad_lines { get; set; } - public bool remove_empty_lines { get; set; } - public string remove_path { get; set; } - public string remove_regex { get; set; } - public string[] search_in { get; set; } - public string type { get; set; } + public string good_regex { get; set; } + public bool just_list_file { get; set; } + public string line_grep { get; set; } + public bool only_bad_lines { get; set; } + public bool remove_empty_lines { get; set; } + public string remove_path { get; set; } + public string remove_regex { get; set; } + public string[] search_in { get; set; } + public string type { get; set; } } public class Variable @@ -92,9 +93,9 @@ public class Variable public string value { get; set; } } - public SearchParams[] search { get; set; } - - public Defaults defaults { get; set; } + public SearchParams[] search { get; set; } + + public Defaults defaults { get; set; } public Variable[] variables { get; set; } } diff --git a/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfigHelper.cs b/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfigHelper.cs index a1f4bc3f5..d6e334497 100644 --- a/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfigHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Helpers/YamlConfig/YamlConfigHelper.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -using System.Yaml.Serialization; using System.IO; -using System.Reflection; using System.Linq; +using System.Reflection; +using System.Yaml.Serialization; using static winPEAS.Helpers.YamlConfig.YamlConfig; -using static winPEAS.Helpers.YamlConfig.YamlRegexConfig; namespace winPEAS.Helpers.YamlConfig @@ -30,7 +29,7 @@ public static YamlRegexConfig GetRegexesSearchConfig() YamlRegexConfig yamlConfig = (YamlRegexConfig)yamlSerializer.Deserialize(configFileContent, typeof(YamlRegexConfig))[0]; // check - if (yamlConfig.regular_expresions == null || yamlConfig.regular_expresions.Length == 0) + if (yamlConfig.regular_expresions == null || yamlConfig.regular_expresions.Length == 0) { throw new System.Exception("No configuration was read"); } @@ -79,7 +78,7 @@ public static YamlConfig GetWindowsSearchConfig() // apply the defaults e.g. for filesearch foreach (var searchItem in yamlConfig.search) - { + { SetDefaultOptions(searchItem, yamlConfig.defaults); } @@ -91,7 +90,7 @@ public static YamlConfig GetWindowsSearchConfig() Beaprint.PrintException($"An exception occured while parsing sensitive_files.yaml configuration file: {e.Message}"); throw; - } + } } private static void SetDefaultOptions(SearchParams searchItem, Defaults defaults) @@ -106,7 +105,7 @@ private static void SetFileOptions(FileParam[] fileParams, Defaults defaults) foreach (var fileParam in fileParams) { var value = fileParam.value; - + value.bad_regex = GetValueOrDefault(value.bad_regex, defaults.bad_regex); value.good_regex = GetValueOrDefault(value.good_regex, defaults.good_regex); value.just_list_file = GetValueOrDefault(value.just_list_file, defaults.just_list_file); @@ -135,7 +134,7 @@ private static void SetFileOptions(FileParam[] fileParams, Defaults defaults) private static T GetValueOrDefault(T val, T defaultValue) { - return val == null ? defaultValue : val; + return val == null ? defaultValue : val; } private static T GetValueOrDefault(Dictionary dict, string key, T defaultValue) diff --git a/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/ApplicationInfoHelper.cs b/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/ApplicationInfoHelper.cs index f5eb95e8b..67ba3cc51 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/ApplicationInfoHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/ApplicationInfoHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Text; using winPEAS.Helpers; using winPEAS.Native; @@ -10,7 +9,7 @@ namespace winPEAS.Info.ApplicationInfo { internal class ApplicationInfoHelper { - + public static string GetActiveWindowTitle() { const int nChars = 256; @@ -46,7 +45,7 @@ void ActOnTask(Task t) { try { - if (t.Enabled && + if (t.Enabled && !string.IsNullOrEmpty(t.Path) && !t.Path.Contains("Microsoft") && !string.IsNullOrEmpty(t.Definition.RegistrationInfo.Author) && !t.Definition.RegistrationInfo.Author.Contains("Microsoft")) diff --git a/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/AutoRuns.cs b/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/AutoRuns.cs index c6b8a3a17..d064d7dd7 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/AutoRuns.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/AutoRuns.cs @@ -1,10 +1,10 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Management; using System.Text.RegularExpressions; -using Microsoft.Win32; using winPEAS.Helpers; using winPEAS.Helpers.Registry; @@ -204,7 +204,7 @@ internal static class AutoRuns { autorunLocationKey[0], autorunLocationKey[1] + "\\" + clsid_name, autorunLocationKey[2] } - : new List {autorunLocationKey[0], autorunLocationKey[1] + "\\" + clsid_name}); + : new List { autorunLocationKey[0], autorunLocationKey[1] + "\\" + clsid_name }); } } @@ -243,10 +243,10 @@ internal static class AutoRuns string folder = Path.GetDirectoryName(filepath_cleaned); try - { + { //If the path doesn't exist, pass if (File.GetAttributes(filepath_cleaned).HasFlag(FileAttributes.Directory)) - { + { //If the path is already a folder, change the values of the params orig_filepath = ""; folder = filepath_cleaned; @@ -336,7 +336,7 @@ internal static class AutoRuns var systemDrive = Environment.GetEnvironmentVariable("SystemDrive"); var autorunLocations = new List { - Environment.ExpandEnvironmentVariables(@"%programdata%\Microsoft\Windows\Start Menu\Programs\Startup"), + Environment.ExpandEnvironmentVariables(@"%programdata%\Microsoft\Windows\Start Menu\Programs\Startup"), }; string usersPath = Path.Combine(Environment.GetEnvironmentVariable(@"USERPROFILE")); @@ -344,15 +344,18 @@ internal static class AutoRuns try { - var userDirs = Directory.EnumerateDirectories(usersPath); - - foreach (var userDir in userDirs) + if (Directory.Exists(usersPath)) { - string startupPath = $@"{userDir}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"; + var userDirs = Directory.EnumerateDirectories(usersPath); - if (Directory.Exists(startupPath)) + foreach (var userDir in userDirs) { - autorunLocations.Add(startupPath); + string startupPath = $@"{userDir}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"; + + if (Directory.Exists(startupPath)) + { + autorunLocations.Add(startupPath); + } } } } @@ -364,22 +367,25 @@ internal static class AutoRuns { try { - var files = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); - - foreach (string filepath in files) + if (Directory.Exists(path)) { - string folder = Path.GetDirectoryName(filepath); - results.Add(new Dictionary() { - { "Reg", "" }, - { "RegKey", "" }, - { "RegPermissions", "" }, - { "Folder", folder }, - { "File", filepath }, - { "isWritableReg", ""}, - { "interestingFolderRights", string.Join(", ", PermissionsHelper.GetPermissionsFolder(folder, Checks.Checks.CurrentUserSiDs))}, - { "interestingFileRights", string.Join(", ", PermissionsHelper.GetPermissionsFile(filepath, Checks.Checks.CurrentUserSiDs))}, - { "isUnquotedSpaced", MyUtils.CheckQuoteAndSpace(path).ToString() } - }); + var files = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); + + foreach (string filepath in files) + { + string folder = Path.GetDirectoryName(filepath); + results.Add(new Dictionary() { + { "Reg", "" }, + { "RegKey", "" }, + { "RegPermissions", "" }, + { "Folder", folder }, + { "File", filepath }, + { "isWritableReg", ""}, + { "interestingFolderRights", string.Join(", ", PermissionsHelper.GetPermissionsFolder(folder, Checks.Checks.CurrentUserSiDs))}, + { "interestingFileRights", string.Join(", ", PermissionsHelper.GetPermissionsFile(filepath, Checks.Checks.CurrentUserSiDs))}, + { "isUnquotedSpaced", MyUtils.CheckQuoteAndSpace(path).ToString() } + }); + } } } catch (Exception) @@ -477,7 +483,7 @@ internal static class AutoRuns private static IEnumerable> GetAutoRunsFiles() { - var results = new List>(); + var results = new List>(); var systemDrive = Environment.GetEnvironmentVariable("SystemDrive"); var autostartFiles = new HashSet { diff --git a/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/InstalledApps.cs b/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/InstalledApps.cs index 85f561bb6..8c7b5ef75 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/InstalledApps.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/ApplicationInfo/InstalledApps.cs @@ -8,7 +8,7 @@ namespace winPEAS.Info.ApplicationInfo { internal static class InstalledApps - { + { public static SortedDictionary> GetInstalledAppsPerms() { //Get from Program Files @@ -71,16 +71,19 @@ internal static class InstalledApps var results = new SortedDictionary>(); try { - foreach (string f in Directory.EnumerateFiles(fpath)) + if (Directory.Exists(fpath)) { - results[f] = new Dictionary + foreach (string f in Directory.EnumerateFiles(fpath)) + { + results[f] = new Dictionary { { f, string.Join(", ", PermissionsHelper.GetPermissionsFile(f, Checks.Checks.CurrentUserSiDs)) } }; - } - foreach (string d in Directory.EnumerateDirectories(fpath)) - { - results[d] = PermissionsHelper.GetRecursivePrivs(d); + } + foreach (string d in Directory.EnumerateDirectories(fpath)) + { + results[d] = PermissionsHelper.GetRecursivePrivs(d); + } } } catch (Exception ex) diff --git a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/Logon.cs b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/Logon.cs index 7d4e7f57a..58d464d5b 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/Logon.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/Logon.cs @@ -18,12 +18,12 @@ public static LogonInfo GetLogonInfos(int lastDays) var kerberosLoggedUsersSet = new HashSet(); string userRegex = null; - + var startTime = DateTime.Now.AddDays(-lastDays); var endTime = DateTime.Now; var query = $@"*[System/EventID=4624] and *[System[TimeCreated[@SystemTime >= '{startTime.ToUniversalTime():o}']]] and *[System[TimeCreated[@SystemTime <= '{endTime.ToUniversalTime():o}']]]"; - var logReader = MyUtils.GetEventLogReader("Security", query); + var logReader = MyUtils.GetEventLogReader("Security", query); // read the event log for (var eventDetail = logReader.ReadEvent(); eventDetail != null; eventDetail = logReader.ReadEvent()) @@ -127,14 +127,14 @@ public static LogonInfo GetLogonInfos(int lastDays) result.NTLMv2LoggedUsersSet = NTLMv2LoggedUsersSet; result.LogonEventInfos = logonEventInfos; - return result; + return result; } public static IEnumerable GetExplicitLogonEventsInfos(int lastDays) { const string eventId = "4648"; string userFilterRegex = null; - + var startTime = DateTime.Now.AddDays(-lastDays); var endTime = DateTime.Now; @@ -143,7 +143,7 @@ public static IEnumerable GetExplicitLogonEventsInfos(in var logReader = MyUtils.GetEventLogReader("Security", query); for (var eventDetail = logReader.ReadEvent(); eventDetail != null; eventDetail = logReader.ReadEvent()) - { + { //string subjectUserSid = eventDetail.GetPropertyValue(0); var subjectUserName = eventDetail.GetPropertyValue(1); var subjectDomainName = eventDetail.GetPropertyValue(2); diff --git a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/LogonEventInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/LogonEventInfo.cs index 8c3ca64db..bf5e4a4e0 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/LogonEventInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/Logon/LogonEventInfo.cs @@ -40,6 +40,6 @@ internal class LogonEventInfo LmPackage = lmPackage; TargetOutboundUserName = targetOutboundUserName; TargetOutboundDomainName = targetOutboundDomainName; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/PowerShell/PowerShell.cs b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/PowerShell/PowerShell.cs index c9350e9ef..1a8c806b6 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/PowerShell/PowerShell.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/PowerShell/PowerShell.cs @@ -16,7 +16,7 @@ public static IEnumerable GetPowerShellEventInfos() string[] powerShellLogs = { "Microsoft-Windows-PowerShell/Operational", "Windows PowerShell" }; // Get our "sensitive" cmdline regexes from a common helper function. - var powerShellRegex = Common.GetInterestingProcessArgsRegex(); + var powerShellRegex = Common.GetInterestingProcessArgsRegex(); foreach (var logName in powerShellLogs) { diff --git a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreation.cs b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreation.cs index 091b9bfaa..c2c9b1161 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreation.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreation.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; using winPEAS.Helpers; -using winPEAS.Info.EventsInfo.PowerShell; namespace winPEAS.Info.EventsInfo.ProcessCreation { internal class ProcessCreation { public static IEnumerable GetProcessCreationEventInfos() - { + { // Get our "sensitive" cmdline regexes from a common helper function. - var processCmdLineRegex = Common.GetInterestingProcessArgsRegex(); + var processCmdLineRegex = Common.GetInterestingProcessArgsRegex(); var query = $"*[System/EventID=4688]"; var logReader = MyUtils.GetEventLogReader("Security", query); @@ -33,6 +32,6 @@ public static IEnumerable GetProcessCreationEventInfos } } } - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreationEventInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreationEventInfo.cs index 7fbb489ba..0da1a1078 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreationEventInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/EventsInfo/ProcessCreation/ProcessCreationEventInfo.cs @@ -19,6 +19,6 @@ internal class ProcessCreationEventInfo EventId = eventId; User = user; Match = match; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/CertificateInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/CertificateInfo.cs index 3937f8c0d..103447f35 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/CertificateInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/CertificateInfo.cs @@ -3,7 +3,7 @@ namespace winPEAS.Info.FilesInfo.Certificates { - internal class CertificateInfo + internal class CertificateInfo { public string StoreLocation { get; set; } public string Issuer { get; set; } diff --git a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/Certificates.cs b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/Certificates.cs index ab7e6947c..c0e86120c 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/Certificates.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Certificates/Certificates.cs @@ -34,19 +34,19 @@ public static IEnumerable GetCertificateInfos() switch (ext.Oid.FriendlyName) { case "Enhanced Key Usage": - { - var extUsages = ((X509EnhancedKeyUsageExtension)ext).EnhancedKeyUsages; + { + var extUsages = ((X509EnhancedKeyUsageExtension)ext).EnhancedKeyUsages; - if (extUsages.Count == 0) - continue; + if (extUsages.Count == 0) + continue; - foreach (var extUsage in extUsages) - { - enhancedKeyUsages.Add(extUsage.FriendlyName); - } + foreach (var extUsage in extUsages) + { + enhancedKeyUsages.Add(extUsage.FriendlyName); + } - break; - } + break; + } case "Certificate Template Name": case "Certificate Template Information": template = ext.Format(false); diff --git a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/McAfee/McAfee.cs b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/McAfee/McAfee.cs index 0f1e9de49..ba373355b 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/McAfee/McAfee.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/McAfee/McAfee.cs @@ -127,7 +127,7 @@ private static string DecryptPassword(string base64password) byte[] XORKey = { 0x12, 0x15, 0x0F, 0x10, 0x11, 0x1C, 0x1A, 0x06, 0x0A, 0x1F, 0x1B, 0x18, 0x17, 0x16, 0x05, 0x19 }; // xor the input b64 string with the static XOR key - var passwordBytes = System.Convert.FromBase64String(base64password); + var passwordBytes = Convert.FromBase64String(base64password); for (var i = 0; i < passwordBytes.Length; i++) { passwordBytes[i] = (byte)(passwordBytes[i] ^ XORKey[i % XORKey.Length]); @@ -137,7 +137,7 @@ private static string DecryptPassword(string base64password) //var tDESKey = MyUtils.CombineArrays(crypto.ComputeHash(System.Text.Encoding.ASCII.GetBytes("")), new byte[] { 0x00, 0x00, 0x00, 0x00 }); byte[] tDESKey = { 62, 241, 54, 184, 179, 59, 239, 188, 52, 38, 167, 181, 78, 196, 26, 55, 124, 211, 25, 155, 0, 0, 0, 0 }; - + // set the options we need var tDESalg = new TripleDESCryptoServiceProvider(); tDESalg.Mode = CipherMode.ECB; diff --git a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Office/Office.cs b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Office/Office.cs index dfda127a3..b0300e5aa 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Office/Office.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/FilesInfo/Office/Office.cs @@ -1,9 +1,9 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; -using Microsoft.Win32; using winPEAS.Helpers; using winPEAS.Helpers.Registry; using winPEAS.Info.FilesInfo.Office.OneDrive; diff --git a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Firewall.cs b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Firewall.cs index 2ca99b938..a706bdbba 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Firewall.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Firewall.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; using System.Runtime.InteropServices; using winPEAS.Helpers; @@ -25,7 +24,7 @@ public static string GetFirewallProfiles() Type firewall = Type.GetTypeFromCLSID(new Guid("E2B3C97F-6AE1-41AC-817A-F6F92166D7DD")); object firewallObj = Activator.CreateInstance(firewall); object types = ReflectionHelper.InvokeMemberProperty(firewallObj, "CurrentProfileTypes"); - result = $"{(FirewallProfiles) int.Parse(types.ToString())}"; + result = $"{(FirewallProfiles)int.Parse(types.ToString())}"; } catch (Exception ex) { diff --git a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettings.cs b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettings.cs index ec4c8168c..83a19b300 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettings.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettings.cs @@ -33,7 +33,7 @@ public static InternetSettingsInfo GetInternetSettingsInfo() string zoneMapKey = @"Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMapKey"; AddSettings("HKCU", zoneMapKey, result.ZoneMaps, zoneMapKeys); AddSettings("HKLM", zoneMapKey, result.ZoneMaps, zoneMapKeys); - + // List Zones settings with automatic logons /** @@ -72,14 +72,14 @@ public static InternetSettingsInfo GetInternetSettingsInfo() authSetting.ToString(), $"{zone} : {authSettingStr}" )); - } + } } return result; } private static void AddSettings(string hive, string keyPath, IList internetSettingsList, IDictionary zoneMapKeys = null) - { + { var proxySettings = (RegistryHelper.GetRegValues(hive, keyPath) ?? new Dictionary()); if (proxySettings != null) { diff --git a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettingsKey.cs b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettingsKey.cs index 6fe1cbb02..78b2855e8 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettingsKey.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/InternetSettings/InternetSettingsKey.cs @@ -19,7 +19,7 @@ internal class InternetSettingsKey Value = value; Interpretation = interpretation; Hive = hive; - Path = path; + Path = path; } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/NetworkInfoHelper.cs b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/NetworkInfoHelper.cs index 5512c47bd..36ee9e7c0 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/NetworkInfoHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/NetworkInfoHelper.cs @@ -17,8 +17,8 @@ class NetworkInfoHelper { // https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket private const int AF_INET = 2; - private const int AF_INET6 = 23; - + private const int AF_INET6 = 23; + [StructLayout(LayoutKind.Sequential)] internal struct MIB_IPNETROW { @@ -191,12 +191,12 @@ public static List> GetNetConnections() foreach (var listener in props.GetActiveTcpListeners()) { bool repeated = false; - foreach(List inside_entry in results) + foreach (List inside_entry in results) { if (inside_entry.SequenceEqual(new List() { "TCP", listener.ToString(), "", "Listening" })) repeated = true; } - if (! repeated) + if (!repeated) results.Add(new List() { "TCP", listener.ToString(), "", "Listening" }); } @@ -218,12 +218,12 @@ public static List> GetNetConnections() } return results; - } - - - - // https://stackoverflow.com/questions/3567063/get-a-list-of-all-unc-shared-folders-on-a-local-network-server - // v2: https://stackoverflow.com/questions/6227892/reading-share-permissions-in-c-sharp + } + + + + // https://stackoverflow.com/questions/3567063/get-a-list-of-all-unc-shared-folders-on-a-local-network-server + // v2: https://stackoverflow.com/questions/6227892/reading-share-permissions-in-c-sharp public static List> GetNetworkShares(string pcname) { List> results = new List>(); @@ -297,8 +297,8 @@ public static List> GetNetConnections() Beaprint.PrintException(ex.Message); } return results; - } - + } + public static List GetTcpConnections(IPVersion ipVersion, Dictionary processesByPid = null) { int bufferSize = 0; @@ -325,8 +325,8 @@ public static List GetTcpConnections(IPVersion ipVersion, Dic // If not zero, the call failed. if (result != 0) - { - return new List(); + { + return new List(); } // Marshals data fron an unmanaged block of memory to the @@ -337,7 +337,7 @@ public static List GetTcpConnections(IPVersion ipVersion, Dic // Determine if IPv4 or IPv6. if (ipVersion == IPVersion.IPv4) { - MIB_TCPTABLE_OWNER_PID tcpRecordsTable = (MIB_TCPTABLE_OWNER_PID) Marshal.PtrToStructure(tcpTableRecordsPtr, typeof(MIB_TCPTABLE_OWNER_PID)); + MIB_TCPTABLE_OWNER_PID tcpRecordsTable = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(tcpTableRecordsPtr, typeof(MIB_TCPTABLE_OWNER_PID)); IntPtr tableRowPtr = (IntPtr)((long)tcpTableRecordsPtr + Marshal.SizeOf(tcpRecordsTable.dwNumEntries)); @@ -373,7 +373,7 @@ public static List GetTcpConnections(IPVersion ipVersion, Dic } else if (ipVersion == IPVersion.IPv6) { - MIB_TCP6TABLE_OWNER_PID tcpRecordsTable = (MIB_TCP6TABLE_OWNER_PID) Marshal.PtrToStructure(tcpTableRecordsPtr, typeof(MIB_TCP6TABLE_OWNER_PID)); + MIB_TCP6TABLE_OWNER_PID tcpRecordsTable = (MIB_TCP6TABLE_OWNER_PID)Marshal.PtrToStructure(tcpTableRecordsPtr, typeof(MIB_TCP6TABLE_OWNER_PID)); IntPtr tableRowPtr = (IntPtr)((long)tcpTableRecordsPtr + Marshal.SizeOf(tcpRecordsTable.dwNumEntries)); @@ -461,14 +461,14 @@ public static IEnumerable GetUdpConnections(IPVersion ipVersi // Determine if IPv4 or IPv6. if (ipVersion == IPVersion.IPv4) { - MIB_UDPTABLE_OWNER_PID udpRecordsTable = (MIB_UDPTABLE_OWNER_PID) Marshal.PtrToStructure(udpTableRecordsPtr, typeof(MIB_UDPTABLE_OWNER_PID)); + MIB_UDPTABLE_OWNER_PID udpRecordsTable = (MIB_UDPTABLE_OWNER_PID)Marshal.PtrToStructure(udpTableRecordsPtr, typeof(MIB_UDPTABLE_OWNER_PID)); IntPtr tableRowPtr = (IntPtr)((long)udpTableRecordsPtr + Marshal.SizeOf(udpRecordsTable.dwNumEntries)); // Read and parse the UDP records from the table and store them in list // 'UdpConnection' structure type objects. for (int i = 0; i < udpRecordsTable.dwNumEntries; i++) { - MIB_UDPROW_OWNER_PID udpRow = (MIB_UDPROW_OWNER_PID) Marshal.PtrToStructure(tableRowPtr, typeof(MIB_UDPROW_OWNER_PID)); + MIB_UDPROW_OWNER_PID udpRow = (MIB_UDPROW_OWNER_PID)Marshal.PtrToStructure(tableRowPtr, typeof(MIB_UDPROW_OWNER_PID)); udpTableRecords.Add(new UdpConnectionInfo( Protocol.UDP, new IPAddress(udpRow.localAddr), diff --git a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDP6TABLE_OWNER_PID.cs b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDP6TABLE_OWNER_PID.cs index aadf10d81..278c63889 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDP6TABLE_OWNER_PID.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDP6TABLE_OWNER_PID.cs @@ -6,7 +6,7 @@ namespace winPEAS.Info.NetworkInfo.Structs public struct MIB_UDP6TABLE_OWNER_PID { public uint dwNumEntries; - [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct,SizeConst = 1)] + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)] public MIB_UDP6ROW_OWNER_PID[] table; } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDPTABLE_OWNER_PID.cs b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDPTABLE_OWNER_PID.cs index 24d85dd9f..93105f5f3 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDPTABLE_OWNER_PID.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/NetworkInfo/Structs/MIB_UDPTABLE_OWNER_PID.cs @@ -6,7 +6,7 @@ namespace winPEAS.Info.NetworkInfo.Structs public struct MIB_UDPTABLE_OWNER_PID { public uint dwNumEntries; - [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct,SizeConst = 1)] + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)] public MIB_UDPROW_OWNER_PID[] table; } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/ProcessInfo/ProcessesInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/ProcessInfo/ProcessesInfo.cs index 00e03159c..103464f09 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/ProcessInfo/ProcessesInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/ProcessInfo/ProcessesInfo.cs @@ -6,7 +6,6 @@ using System.Management; using System.Runtime.InteropServices; using System.Security.Principal; -using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using winPEAS.Helpers; @@ -33,7 +32,7 @@ select new Proc = p, Pth = (string)mo["ExecutablePath"], CommLine = (string)mo["CommandLine"], - Owner = Helpers.HandlesHelper.GetProcU(p)["name"], //Needed inside the next foreach + Owner = HandlesHelper.GetProcU(p)["name"], //Needed inside the next foreach }; foreach (var itm in queRy) @@ -54,14 +53,16 @@ select new } if ((string.IsNullOrEmpty(companyName)) || (!Regex.IsMatch(companyName, @"^Microsoft.*", RegexOptions.IgnoreCase))) { - Dictionary to_add = new Dictionary(); - to_add["Name"] = itm.Proc.ProcessName; - to_add["ProcessID"] = itm.Proc.Id.ToString(); - to_add["ExecutablePath"] = itm.Pth; - to_add["Product"] = companyName; - to_add["Owner"] = itm.Owner == null ? "" : itm.Owner; - to_add["isDotNet"] = isDotNet; - to_add["CommandLine"] = itm.CommLine; + Dictionary to_add = new Dictionary + { + ["Name"] = itm.Proc.ProcessName, + ["ProcessID"] = itm.Proc.Id.ToString(), + ["ExecutablePath"] = itm.Pth, + ["Product"] = companyName, + ["Owner"] = itm.Owner == null ? "" : itm.Owner, + ["isDotNet"] = isDotNet, + ["CommandLine"] = itm.CommLine + }; f_results.Add(to_add); } } @@ -123,11 +124,13 @@ select new string hName = HandlesHelper.GetObjectName(dupHandle); - Dictionary to_add = new Dictionary(); - to_add["Handle Name"] = hName; - to_add["Handle"] = h.HandleValue.ToString() + "(" + typeName + ")"; - to_add["Handle Owner"] = "Pid is " + h.UniqueProcessId.ToString() + "(" + origProcInfo.name + ") with owner: " + origProcInfo.userName; - to_add["Reason"] = handlerExp.reason; + Dictionary to_add = new Dictionary + { + ["Handle Name"] = hName, + ["Handle"] = h.HandleValue.ToString() + "(" + typeName + ")", + ["Handle Owner"] = "Pid is " + h.UniqueProcessId.ToString() + "(" + origProcInfo.name + ") with owner: " + origProcInfo.userName, + ["Reason"] = handlerExp.reason + }; if (typeName == "process" || typeName == "thread") { @@ -177,7 +180,7 @@ select new string sFilePath = fni.FileName; if (sFilePath.Length == 0) continue; - + List permsFile = PermissionsHelper.GetPermissionsFile(sFilePath, Checks.Checks.CurrentUserSiDs, PermissionType.WRITEABLE_OR_EQUIVALENT); try { @@ -208,13 +211,13 @@ select new else if (typeName == "key") { HandlesHelper.KEY_RELEVANT_INFO kri = HandlesHelper.getKeyHandlerInfo(dupHandle); - if (kri.path.Length == 0 && kri.hive != null && kri.hive.Length> 0) + if (kri.path.Length == 0 && kri.hive != null && kri.hive.Length > 0) continue; RegistryKey regKey = Helpers.Registry.RegistryHelper.GetReg(kri.hive, kri.path); if (regKey == null) continue; - + List permsReg = PermissionsHelper.GetMyPermissionsR(regKey, Checks.Checks.CurrentUserSiDs); // If current user already have permissions over that reg, handle not interesting to elevate privs diff --git a/winPEAS/winPEASexe/winPEAS/Info/ServicesInfo/ServicesInfoHelper.cs b/winPEAS/winPEASexe/winPEAS/Info/ServicesInfo/ServicesInfoHelper.cs index 9fe114000..324ecfc65 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/ServicesInfo/ServicesInfoHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/ServicesInfo/ServicesInfoHelper.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -8,10 +9,8 @@ using System.Security.AccessControl; using System.ServiceProcess; using System.Text.RegularExpressions; -using Microsoft.Win32; using winPEAS.Helpers; using winPEAS.Helpers.Registry; -using winPEAS.KnownFileCreds; using winPEAS.Native; namespace winPEAS.Info.ServicesInfo @@ -51,17 +50,18 @@ class ServicesInfoHelper if (string.IsNullOrEmpty(companyName) || (!Regex.IsMatch(companyName, @"^Microsoft.*", RegexOptions.IgnoreCase))) { - Dictionary toadd = new Dictionary(); - - toadd["Name"] = GetStringOrEmpty(result["Name"]); - toadd["DisplayName"] = GetStringOrEmpty(result["DisplayName"]); - toadd["CompanyName"] = companyName; - toadd["State"] = GetStringOrEmpty(result["State"]); - toadd["StartMode"] = GetStringOrEmpty(result["StartMode"]); - toadd["PathName"] = GetStringOrEmpty(result["PathName"]); - toadd["FilteredPath"] = binaryPath; - toadd["isDotNet"] = isDotNet; - toadd["Description"] = GetStringOrEmpty(result["Description"]); + Dictionary toadd = new Dictionary + { + ["Name"] = GetStringOrEmpty(result["Name"]), + ["DisplayName"] = GetStringOrEmpty(result["DisplayName"]), + ["CompanyName"] = companyName, + ["State"] = GetStringOrEmpty(result["State"]), + ["StartMode"] = GetStringOrEmpty(result["StartMode"]), + ["PathName"] = GetStringOrEmpty(result["PathName"]), + ["FilteredPath"] = binaryPath, + ["isDotNet"] = isDotNet, + ["Description"] = GetStringOrEmpty(result["Description"]) + }; results.Add(toadd); } @@ -166,7 +166,7 @@ private static string GetStringOrEmpty(object obj) } return results; } - + public static Dictionary GetModifiableServices(Dictionary SIDs) { Dictionary results = new Dictionary(); @@ -222,7 +222,7 @@ private static string GetStringOrEmpty(object obj) { //https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.commonace?view=net-6.0 int serviceRights = ace.AccessMask; string current_perm_str = PermissionsHelper.PermInt2Str(serviceRights, PermissionType.WRITEABLE_OR_EQUIVALENT_SVC); - + if (!string.IsNullOrEmpty(current_perm_str) && !permissions.Contains(current_perm_str)) permissions.Add(current_perm_str); } @@ -232,7 +232,7 @@ private static string GetStringOrEmpty(object obj) if (permissions.Count > 0) { string perms = String.Join(", ", permissions); - if (perms.Replace("Start", "").Replace("Stop","").Length > 3) //Check if any other permissions appart from Start and Stop + if (perms.Replace("Start", "").Replace("Stop", "").Length > 3) //Check if any other permissions appart from Start and Stop results.Add(sc.ServiceName, perms); } @@ -249,9 +249,9 @@ private static string GetStringOrEmpty(object obj) /////// Find Write reg. Services //////// ////////////////////////////////////////// /// Find Services which Reg you have write or equivalent access - public static List> GetWriteServiceRegs(Dictionary NtAccountNames) + public static List> GetWriteServiceRegs(Dictionary NtAccountNames) { - List> results = new List>(); + List> results = new List>(); try { RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"system\currentcontrolset\services"); @@ -275,7 +275,7 @@ private static string GetStringOrEmpty(object obj) return results; } - + ////////////////////////////////////// //////// PATH DLL Hijacking ///////// ////////////////////////////////////// @@ -294,7 +294,7 @@ private static string GetStringOrEmpty(object obj) foreach (string folder in folders) results[folder] = String.Join(", ", PermissionsHelper.GetPermissionsFolder(folder, Checks.Checks.CurrentUserSiDs)); - + } catch (Exception ex) { diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/CredentialGuard.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/CredentialGuard.cs index 42c5216f5..e2eb8824f 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/CredentialGuard.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/CredentialGuard.cs @@ -35,7 +35,7 @@ internal static void PrintInfo() { var configCheck = (int[])result.GetPropertyValue("SecurityServicesConfigured"); var serviceCheck = (int[])result.GetPropertyValue("SecurityServicesRunning"); - + var configured = false; var running = false; @@ -56,7 +56,7 @@ internal static void PrintInfo() $" Configured: {configured}\n" + $" Running: {running}", colors); - + } } } @@ -68,7 +68,7 @@ internal static void PrintInfo() catch (Exception ex) { //Beaprint.PrintException(ex.Message); - } + } } private static string GetVbsSettingString(uint? vbs) diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/DotNet/DotNet.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/DotNet/DotNet.cs index 2a1714cec..87b8abc46 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/DotNet/DotNet.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/DotNet/DotNet.cs @@ -38,7 +38,7 @@ public static DotNetInfo GetDotNetInfo() private static string GetOSVersion() { - + try { using (var wmiData = new ManagementObjectSearcher(@"root\cimv2", "SELECT Version FROM Win32_OperatingSystem")) diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/GroupPolicy/GroupPolicy.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/GroupPolicy/GroupPolicy.cs index 0397e6ead..df71b896d 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/GroupPolicy/GroupPolicy.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/GroupPolicy/GroupPolicy.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using Microsoft.Win32; +using Microsoft.Win32; +using System.Collections.Generic; using winPEAS.Helpers.Registry; using winPEAS.Native.Enums; @@ -14,7 +14,7 @@ public static IEnumerable GetLocalGroupPolicyInfos() // local machine GPOs var basePath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\DataStore\Machine\0"; var machineIDs = RegistryHelper.GetRegSubkeys("HKLM", basePath) ?? new string[] { }; - + foreach (var id in machineIDs) { var settings = RegistryHelper.GetRegValues("HKLM", $"{basePath}\\{id}"); diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/NamedPipes/NamedPipes.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/NamedPipes/NamedPipes.cs index 5dd56f9f1..4f578e010 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/NamedPipes/NamedPipes.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/NamedPipes/NamedPipes.cs @@ -3,7 +3,6 @@ using System.Runtime.InteropServices; using System.Security.AccessControl; using winPEAS.Native; -using System.Security.Principal; namespace winPEAS.Info.SystemInfo.NamedPipes @@ -51,7 +50,7 @@ public static IEnumerable GetNamedPipeInfos() { var security = File.GetAccessControl($"\\\\.\\pipe\\{namedPipe}"); sddl = security.GetSecurityDescriptorSddlForm(AccessControlSections.All); - List currentUserPermsList = winPEAS.Helpers.PermissionsHelper.GetMyPermissionsF(security, winPEAS.Checks.Checks.CurrentUserSiDs); + List currentUserPermsList = Helpers.PermissionsHelper.GetMyPermissionsF(security, Checks.Checks.CurrentUserSiDs); currentUserPerms = string.Join(", ", currentUserPermsList); } catch diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Ntlm/NtlmSettingsInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Ntlm/NtlmSettingsInfo.cs index b30bbb7e0..33b945b10 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Ntlm/NtlmSettingsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Ntlm/NtlmSettingsInfo.cs @@ -5,7 +5,7 @@ internal class NtlmSettingsInfo public uint? LanmanCompatibilityLevel { get; set; } public string LanmanCompatibilityLevelString - { + { get { switch (LanmanCompatibilityLevel) @@ -25,11 +25,11 @@ public string LanmanCompatibilityLevelString public bool ClientRequireSigning { get; set; } public bool ClientNegotiateSigning { get; set; } public bool ServerRequireSigning { get; set; } - public bool ServerNegotiateSigning { get; set; } + public bool ServerNegotiateSigning { get; set; } public uint? LdapSigning { get; set; } public string LdapSigningString - { + { get { switch (LdapSigning) @@ -44,7 +44,7 @@ public string LdapSigningString } public uint? NTLMMinClientSec { get; set; } - public uint? NTLMMinServerSec { get; set; } + public uint? NTLMMinServerSec { get; set; } public uint? InboundRestrictions { get; internal set; } public string InboundRestrictionsString diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShell.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShell.cs index 4aeb41fc9..bf2475b6f 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShell.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShell.cs @@ -8,7 +8,7 @@ namespace winPEAS.Info.SystemInfo.PowerShell internal class PowerShell { public static IEnumerable GetPowerShellSessionSettingsInfos() - { + { var plugins = new[] { "Microsoft.PowerShell", "Microsoft.PowerShell.Workflow", "Microsoft.PowerShell32" }; foreach (var plugin in plugins) @@ -49,6 +49,6 @@ public static IEnumerable GetPowerShellSessionSet yield return new PowerShellSessionSettingsInfo(plugin, access); } - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShellSessionSettingsInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShellSessionSettingsInfo.cs index 26aa238fb..b76097e7a 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShellSessionSettingsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/PowerShell/PowerShellSessionSettingsInfo.cs @@ -11,6 +11,6 @@ public PowerShellSessionSettingsInfo(string plugin, List permi { Plugin = plugin; Permissions = permissions; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Printers/Printers.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Printers/Printers.cs index 9dda054d5..6eecad4ea 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Printers/Printers.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/Printers/Printers.cs @@ -10,14 +10,14 @@ namespace winPEAS.Info.SystemInfo.Printers { internal class Printers - { + { [StructLayout(LayoutKind.Sequential)] public struct SECURITY_INFOS { public string Owner; public RawSecurityDescriptor SecurityDescriptor; public string SDDL; - } + } public static IEnumerable GetPrinterWMIInfos() { diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysMon.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysMon.cs index bdeae0bb8..739b5b019 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysMon.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysMon.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.Eventing.Reader; -using System.Text.RegularExpressions; using winPEAS.Helpers; using winPEAS.Helpers.Registry; @@ -14,7 +13,7 @@ static class SysMon public static IEnumerable GetSysMonInfos() { - var paramsKey = @"SYSTEM\CurrentControlSet\Services\SysmonDrv\Parameters"; + var paramsKey = @"SYSTEM\CurrentControlSet\Services\SysmonDrv\Parameters"; uint? regHashAlg = GetUintNullableFromString(RegistryHelper.GetRegValue("HKLM", paramsKey, "HashingAlgorithm")); uint? regOptions = GetUintNullableFromString(RegistryHelper.GetRegValue("HKLM", paramsKey, "Options")); byte[] regSysmonRules = GetBinaryValueFromRegistry(Registry.LocalMachine, paramsKey, "Rules"); diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysmonInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysmonInfo.cs index b0115e9fa..a19e51d6a 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysmonInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SysMon/SysmonInfo.cs @@ -13,6 +13,6 @@ public SysmonInfo(bool installed, SysmonHashAlgorithm hashingAlgorithm, SysmonOp HashingAlgorithm = hashingAlgorithm; Options = options; Rules = rules; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SystemInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SystemInfo.cs index c3b67aa9b..a2d7aaaee 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SystemInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/SystemInfo.cs @@ -9,7 +9,6 @@ using System.Windows.Forms; using winPEAS.Helpers; using winPEAS.Helpers.Registry; -using winPEAS.KnownFileCreds; namespace winPEAS.Info.SystemInfo { @@ -160,7 +159,7 @@ public static bool IsVirtualMachine() { Dictionary results = new Dictionary(); string whitelistpaths = ""; - + try { var keys = RegistryHelper.GetRegValues("HKLM", @"SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths"); @@ -188,7 +187,7 @@ public static bool IsVirtualMachine() { results["whitelistpaths"] = " " + whitelistpaths; //Add this info the last } - + return results; } @@ -342,7 +341,7 @@ private static IEnumerable GetPowerShellCoreVersions() { var keys = RegistryHelper.GetRegSubkeys("HKLM", @"SOFTWARE\Microsoft\PowerShellCore\InstalledVersions\") ?? new string[] { }; - return keys.Select(key => + return keys.Select(key => RegistryHelper.GetRegValue("HKLM", @"SOFTWARE\Microsoft\PowerShellCore\InstalledVersions\" + key, "SemanticVersion")) .Where(version => version != null).ToList(); } @@ -461,7 +460,7 @@ private static IEnumerable GetPowerShellCoreVersions() if ((settings != null) && (settings.Count != 0)) { foreach (KeyValuePair kvp in settings) - { + { result[kvp.Key] = (string)kvp.Value; } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettings.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettings.cs index 6a3786771..aef3b712b 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettings.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettings.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Microsoft.Win32; -using winPEAS.Helpers; using winPEAS.Helpers.Registry; namespace winPEAS.Info.SystemInfo.WindowsDefender @@ -17,14 +15,14 @@ internal class WindowsDefenderSettings public WindowsDefenderSettings(string defenderBaseKeyPath) { PathExclusions = new List(); - var pathExclusionData = RegistryHelper.GetRegValues("HKLM", $"{ defenderBaseKeyPath}\\Exclusions\\Paths"); + var pathExclusionData = RegistryHelper.GetRegValues("HKLM", $"{defenderBaseKeyPath}\\Exclusions\\Paths"); if (pathExclusionData != null) { foreach (var kvp in pathExclusionData) { PathExclusions.Add(kvp.Key); } - } + } PolicyManagerPathExclusions = new List(); var excludedPaths = RegistryHelper.GetRegValue("HKLM", $"{defenderBaseKeyPath}\\Policy Manager", "ExcludedPaths"); @@ -54,7 +52,7 @@ public WindowsDefenderSettings(string defenderBaseKeyPath) { ExtensionExclusions.Add(kvp.Key); } - } + } var asrKeyPath = $"{defenderBaseKeyPath}\\Windows Defender Exploit Guard\\ASR"; var asrEnabled = RegistryHelper.GetRegValue("HKLM", asrKeyPath, "ExploitGuard_ASR_Rules"); @@ -82,7 +80,7 @@ public WindowsDefenderSettings(string defenderBaseKeyPath) { AsrSettings.Exclusions.Add(value.Key); } - } + } } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettingsInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettingsInfo.cs index 1845a56b9..a4c65b44b 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettingsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/SystemInfo/WindowsDefender/WindowsDefenderSettingsInfo.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace winPEAS.Info.SystemInfo.WindowsDefender +namespace winPEAS.Info.SystemInfo.WindowsDefender { class WindowsDefenderSettingsInfo { diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessions.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessions.cs index e620ab2a2..15566de98 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessions.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessions.cs @@ -184,5 +184,5 @@ private static IEnumerable GetLogonSessionsInfoWMI() } } } - } + } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessionsInfo.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessionsInfo.cs index 18ec84ccd..239814d47 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessionsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/LogonSessions/LogonSessionsInfo.cs @@ -43,6 +43,6 @@ internal class LogonSessionsInfo LogonServerDnsDomain = logonServerDnsDomain; UserPrincipalName = userPrincipalName; UserSID = userSid; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SAM/SamServer.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SAM/SamServer.cs index a7123612b..f89c9cfbf 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SAM/SamServer.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SAM/SamServer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Security.Principal; -using winPEAS.Helpers; using winPEAS.Native; using winPEAS.Native.Classes; @@ -99,9 +98,9 @@ public IEnumerable EnumerateDomains() yield return us.ToString(); us.Buffer = IntPtr.Zero; // we don't own this one } - } + } + - private static void Check(NTSTATUS err) { diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SID2GroupNameHelper.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SID2GroupNameHelper.cs index 5b34b9dc2..f6f0fd265 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SID2GroupNameHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/SID2GroupNameHelper.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Net.NetworkInformation; using System.Security.Principal; -using System.Text.RegularExpressions; using winPEAS.Helpers; namespace winPEAS.Info.UserInfo diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Tenant/Tenant.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Tenant/Tenant.cs index 9ba26ab9c..a8a62f1e2 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Tenant/Tenant.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Tenant/Tenant.cs @@ -3,7 +3,6 @@ using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Text; -using winPEAS.Helpers; using winPEAS.Native; using winPEAS.Native.Structs; diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Enums.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Enums.cs index 513f5f1d8..390567dbb 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Enums.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Enums.cs @@ -1,7 +1,7 @@ using System; namespace winPEAS.Info.UserInfo.Token -{ +{ [Flags] public enum LuidAttributes : uint { diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Token.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Token.cs index 638460b33..685b3ed1d 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Token.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/Token/Token.cs @@ -10,7 +10,7 @@ namespace winPEAS.Info.UserInfo.Token { internal static class Token - { + { public static Dictionary GetTokenGroupPrivs() { // Returns all privileges that the current process/user possesses @@ -36,7 +36,7 @@ internal static class Token Advapi32.LookupPrivilegeName(null, luidPointer, null, ref luidNameLen); strBuilder.EnsureCapacity(luidNameLen + 1); if (Advapi32.LookupPrivilegeName(null, luidPointer, strBuilder, ref luidNameLen)) - results[strBuilder.ToString()] = $"{(LuidAttributes) laa.Attributes}"; + results[strBuilder.ToString()] = $"{(LuidAttributes)laa.Attributes}"; Marshal.FreeHGlobal(luidPointer); } } diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/User.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/User.cs index 67cd345be..bb8d4b4ab 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/User.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/User.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using System.Security.Principal; using winPEAS.Helpers; -using winPEAS.KnownFileCreds; using winPEAS.Native; using winPEAS.Native.Structs; @@ -18,7 +17,7 @@ internal static class User public static List GetMachineUsers(bool onlyActive, bool onlyDisabled, bool onlyLockout, bool onlyAdmins, bool fullInfo) { List retList = new List(); - + try { foreach (ManagementObject user in Checks.Checks.Win32Users) @@ -107,7 +106,7 @@ public static UserPrincipal GetUser(string sUserName, string domain) } } catch - { + { //If error, then some error ocurred trying to find a user inside an unexistant domain, check if local user user = GetUserLocal(sUserName); } diff --git a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/UserInfoHelper.cs b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/UserInfoHelper.cs index 6cc8dd661..06de1214c 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/UserInfo/UserInfoHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/UserInfo/UserInfoHelper.cs @@ -6,7 +6,6 @@ using winPEAS.Helpers; using winPEAS.Helpers.Registry; using winPEAS.Info.UserInfo.SAM; -using winPEAS.KnownFileCreds; using winPEAS.Native; using winPEAS.Native.Enums; @@ -14,12 +13,12 @@ //I have also created the folder Costura32 and Costura64 with the respective Dlls of Colorful.Console namespace winPEAS.Info.UserInfo -{ +{ class UserInfoHelper { // https://stackoverflow.com/questions/5247798/get-list-of-local-computer-usernames-in-windows - - + + public static string SID2GroupName(string SID) { //Frist, look in well-known SIDs @@ -84,13 +83,13 @@ public static string GetSIDGroupName(string SID, ContextType ct) Beaprint.PrintException(ex.Message); } return groupName; - } + } public static PrincipalContext GetPrincipalContext() { PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine); return oPrincipalContext; - } + } //From Seatbelt public enum WTS_CONNECTSTATE_CLASS @@ -106,7 +105,7 @@ public enum WTS_CONNECTSTATE_CLASS Down, Init } - + public static void CloseServer(IntPtr ServerHandle) { Wtsapi32.WTSCloseServer(ServerHandle); @@ -145,7 +144,7 @@ private struct WTS_SESSION_INFO_1 [MarshalAs(UnmanagedType.LPStr)] public String pFarmName; } - + public static IntPtr OpenServer(String Name) { IntPtr server = Wtsapi32.WTSOpenServer(Name); @@ -215,7 +214,7 @@ public static IntPtr OpenServer(String Name) } return results; } - + // https://stackoverflow.com/questions/31464835/how-to-programmatically-check-the-password-must-meet-complexity-requirements-g public static List> GetPasswordPolicy() { @@ -247,18 +246,19 @@ public static IntPtr OpenServer(String Name) Beaprint.GrayPrint(string.Format(" [X] Exception: {0}", ex)); } return results; - } - + } + public static Dictionary GetAutoLogon() { - Dictionary results = new Dictionary(); - - results["DefaultDomainName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultDomainName"); - results["DefaultUserName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultUserName"); - results["DefaultPassword"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultPassword"); - results["AltDefaultDomainName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AltDefaultDomainName"); - results["AltDefaultUserName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AltDefaultUserName"); - results["AltDefaultPassword"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AltDefaultPassword"); + Dictionary results = new Dictionary + { + ["DefaultDomainName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultDomainName"), + ["DefaultUserName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultUserName"), + ["DefaultPassword"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultPassword"), + ["AltDefaultDomainName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AltDefaultDomainName"), + ["AltDefaultUserName"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AltDefaultUserName"), + ["AltDefaultPassword"] = RegistryHelper.GetRegValue("HKLM", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AltDefaultPassword") + }; return results; } @@ -281,7 +281,7 @@ public static string GetClipboardText() c = $"{Clipboard.GetFileDropList()}"; //else if (Clipboard.ContainsImage()) //No system.Drwing import - //c = string.Format("{0}", Clipboard.GetImage()); + //c = string.Format("{0}", Clipboard.GetImage()); } catch (Exception ex) { diff --git a/winPEAS/winPEASexe/winPEAS/Info/WindowsCreds/RDPServerSettings.cs b/winPEAS/winPEASexe/winPEAS/Info/WindowsCreds/RDPServerSettings.cs index e9b3ac93d..8eafecb04 100644 --- a/winPEAS/winPEASexe/winPEAS/Info/WindowsCreds/RDPServerSettings.cs +++ b/winPEAS/winPEASexe/winPEAS/Info/WindowsCreds/RDPServerSettings.cs @@ -29,6 +29,6 @@ internal class RDPServerSettings AllowSmartCardRedirection = allowSmartCardRedirection; BlockPnPDeviceRedirection = blockPnPDeviceRedirection; BlockPrinterRedirection = blockPrinterRedirection; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/InterestingFiles/GPP.cs b/winPEAS/winPEASexe/winPEAS/InterestingFiles/GPP.cs index e16f199bf..1af5ba6da 100644 --- a/winPEAS/winPEASexe/winPEAS/InterestingFiles/GPP.cs +++ b/winPEAS/winPEASexe/winPEAS/InterestingFiles/GPP.cs @@ -15,7 +15,7 @@ internal static class GPP try { - string allUsers = System.Environment.GetEnvironmentVariable("ALLUSERSPROFILE"); + string allUsers = Environment.GetEnvironmentVariable("ALLUSERSPROFILE"); if (!allUsers.Contains("ProgramData")) { @@ -225,11 +225,13 @@ internal static class GPP Changed = "[BLANK]"; } - results[file] = new Dictionary(); - results[file]["UserName"] = UserName; - results[file]["NewName"] = NewName; - results[file]["cPassword"] = cPassword; - results[file]["Changed"] = Changed; + results[file] = new Dictionary + { + ["UserName"] = UserName, + ["NewName"] = NewName, + ["cPassword"] = cPassword, + ["Changed"] = Changed + }; } } catch (Exception ex) diff --git a/winPEAS/winPEASexe/winPEAS/InterestingFiles/InterestingFiles.cs b/winPEAS/winPEASexe/winPEAS/InterestingFiles/InterestingFiles.cs index a7a60f37c..a3256255e 100644 --- a/winPEAS/winPEASexe/winPEAS/InterestingFiles/InterestingFiles.cs +++ b/winPEAS/winPEASexe/winPEAS/InterestingFiles/InterestingFiles.cs @@ -9,9 +9,9 @@ namespace winPEAS.InterestingFiles { internal static class InterestingFiles - { + { public static List GetSAMBackups() - { + { //From SharpUP var results = new List(); @@ -28,7 +28,7 @@ public static List GetSAMBackups() $@"{systemRoot}\System32\config\RegBack\SYSTEM", }; - results.AddRange(searchLocations.Where(searchLocation => System.IO.File.Exists(searchLocation))); + results.AddRange(searchLocations.Where(searchLocation => File.Exists(searchLocation))); } catch (Exception ex) { @@ -40,7 +40,7 @@ public static List GetSAMBackups() public static List GetLinuxShells() { var results = new List(); - + try { string drive = Environment.GetEnvironmentVariable("SystemDrive"); @@ -90,7 +90,7 @@ public static List ListUsersDocs() Beaprint.GrayPrint("Error: " + ex); } return results; - } + } public static List> GetRecycleBin() { @@ -102,7 +102,7 @@ public static List ListUsersDocs() // Reference: https://stackoverflow.com/questions/18071412/list-filenames-in-the-recyclebin-with-c-sharp-without-using-any-external-files int lastDays = 30; - var startTime = System.DateTime.Now.AddDays(-lastDays); + var startTime = DateTime.Now.AddDays(-lastDays); // Shell COM object GUID Type shell = Type.GetTypeFromCLSID(new Guid("13709620-C279-11CE-A49E-444553540000")); diff --git a/winPEAS/winPEASexe/winPEAS/InterestingFiles/Unattended.cs b/winPEAS/winPEASexe/winPEAS/InterestingFiles/Unattended.cs index ad84326ec..0114b5768 100644 --- a/winPEAS/winPEASexe/winPEAS/InterestingFiles/Unattended.cs +++ b/winPEAS/winPEASexe/winPEAS/InterestingFiles/Unattended.cs @@ -40,7 +40,7 @@ public static List GetUnattendedInstallFiles() try { - var winDir = System.Environment.GetEnvironmentVariable("windir"); + var winDir = Environment.GetEnvironmentVariable("windir"); string[] searchLocations = { $"{winDir}\\sysprep\\sysprep.xml", @@ -56,7 +56,7 @@ public static List GetUnattendedInstallFiles() $"{winDir}\\..\\unattend.inf", }; - results.AddRange(searchLocations.Where(System.IO.File.Exists)); + results.AddRange(searchLocations.Where(File.Exists)); } catch (Exception ex) { diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/BrowserBase.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/BrowserBase.cs index 987f85a1b..08119b33c 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/BrowserBase.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/BrowserBase.cs @@ -10,7 +10,7 @@ internal abstract class BrowserBase : IBrowser public abstract string Name { get; } public abstract IEnumerable GetSavedCredentials(); public abstract void PrintInfo(); - + public virtual void PrintSavedCredentials() { diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Chrome/Chrome.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Chrome/Chrome.cs index 53d493b9b..d79cbdfbb 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Chrome/Chrome.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Chrome/Chrome.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using System.Web.Script.Serialization; using winPEAS.Checks; @@ -27,7 +28,7 @@ private static void PrintDBsChrome() { Beaprint.MainPrint("Looking for Chrome DBs"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history"); - Dictionary chromeDBs = Chrome.GetChromeDbs(); + Dictionary chromeDBs = GetChromeDbs(); if (chromeDBs.ContainsKey("userChromeCookiesPath")) { @@ -59,7 +60,7 @@ private static void PrintHistBookChrome() { Beaprint.MainPrint("Looking for GET credentials in Chrome history"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history"); - Dictionary> chromeHistBook = Chrome.GetChromeHistBook(); + Dictionary> chromeHistBook = GetChromeHistBook(); List history = chromeHistBook["history"]; List bookmarks = chromeHistBook["bookmarks"]; @@ -77,8 +78,11 @@ private static void PrintHistBookChrome() Beaprint.AnsiPrint(" " + url, colorsB); } } - Console.WriteLine(); + + int limit = 50; + Beaprint.MainPrint($"Chrome history -- limit {limit}\n"); + Beaprint.ListPrint(history.Take(limit).ToList()); } else { @@ -130,14 +134,14 @@ private static void PrintHistBookChrome() else { string userChromeCookiesPath = - $"{System.Environment.GetEnvironmentVariable("USERPROFILE")}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies"; + $"{Environment.GetEnvironmentVariable("USERPROFILE")}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies"; if (File.Exists(userChromeCookiesPath)) { results["userChromeCookiesPath"] = userChromeCookiesPath; } string userChromeLoginDataPath = - $"{System.Environment.GetEnvironmentVariable("USERPROFILE")}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"; + $"{Environment.GetEnvironmentVariable("USERPROFILE")}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"; if (File.Exists(userChromeLoginDataPath)) { results["userChromeLoginDataPath"] = userChromeLoginDataPath; @@ -156,7 +160,7 @@ private static List ParseChromeHistory(string path) List results = new List(); // parses a Chrome history file via regex - if (System.IO.File.Exists(path)) + if (File.Exists(path)) { Regex historyRegex = new Regex(@"(http|ftp|https|file)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?"); @@ -217,10 +221,10 @@ private static List ParseChromeHistory(string path) } else { - string userChromeHistoryPath = string.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\History", System.Environment.GetEnvironmentVariable("USERPROFILE")); + string userChromeHistoryPath = string.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\History", Environment.GetEnvironmentVariable("USERPROFILE")); results["history"] = ParseChromeHistory(userChromeHistoryPath); - string userChromeBookmarkPath = string.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Bookmarks", System.Environment.GetEnvironmentVariable("USERPROFILE")); + string userChromeBookmarkPath = string.Format("{0}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Bookmarks", Environment.GetEnvironmentVariable("USERPROFILE")); results["bookmarks"] = ParseChromeBookmarks(userChromeBookmarkPath); } @@ -241,7 +245,7 @@ private static List ParseChromeBookmarks(string path) { try { - string contents = System.IO.File.ReadAllText(path); + string contents = File.ReadAllText(path); // reference: http://www.tomasvera.com/programming/using-javascriptserializer-to-parse-json-objects/ JavaScriptSerializer json = new JavaScriptSerializer(); diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFDecryptor.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFDecryptor.cs index c401da5a9..fb621c0ad 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFDecryptor.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFDecryptor.cs @@ -9,9 +9,9 @@ namespace winPEAS.KnownFileCreds.Browsers.Firefox /// Firefox helper class /// static class FFDecryptor - { + { static IntPtr NSS3; - + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate long DLLFunctionDelegate(string configdir); diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFLogins.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFLogins.cs index c028ffd01..9a560d9f1 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFLogins.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/FFLogins.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace winPEAS.KnownFileCreds.Browsers.Firefox +namespace winPEAS.KnownFileCreds.Browsers.Firefox { class FFLogins { diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/Firefox.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/Firefox.cs index c2f9c3d3f..31d6c61f5 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/Firefox.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/Firefox/Firefox.cs @@ -4,11 +4,11 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Web.Script.Serialization; +using winPEAS._3rdParty.SQLite; using winPEAS.Checks; using winPEAS.Helpers; using winPEAS.KnownFileCreds.Browsers.Models; -using winPEAS._3rdParty.SQLite; -using System.Web.Script.Serialization; namespace winPEAS.KnownFileCreds.Browsers.Firefox { @@ -29,7 +29,7 @@ private static void PrintDBsFirefox() { Beaprint.MainPrint("Looking for Firefox DBs"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history"); - List firefoxDBs = Firefox.GetFirefoxDbs(); + List firefoxDBs = GetFirefoxDbs(); if (firefoxDBs.Count > 0) { foreach (string firefoxDB in firefoxDBs) //No Beaprints because line needs red @@ -56,21 +56,26 @@ private static void PrintHistFirefox() { Beaprint.MainPrint("Looking for GET credentials in Firefox history"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history"); - List firefoxHist = Firefox.GetFirefoxHistory(); - if (firefoxHist.Count > 0) + List history = GetFirefoxHistory(); + if (history.Count > 0) { Dictionary colorsB = new Dictionary() { { Globals.PrintCredStrings, Beaprint.ansi_color_bad }, }; - foreach (string url in firefoxHist) + foreach (string url in history) { if (MyUtils.ContainsAnyRegex(url.ToUpper(), Browser.CredStringsRegex)) { Beaprint.AnsiPrint(" " + url, colorsB); } } + Console.WriteLine(); + + int limit = 50; + Beaprint.MainPrint($"Firefox history -- limit {limit}\n"); + Beaprint.ListPrint(history.Take(limit).ToList()); } else { @@ -101,7 +106,7 @@ private static List GetFirefoxDbs() if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { string userFirefoxBasePath = $"{dir}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\"; - if (System.IO.Directory.Exists(userFirefoxBasePath)) + if (Directory.Exists(userFirefoxBasePath)) { var directories = Directory.EnumerateDirectories(userFirefoxBasePath); foreach (string directory in directories) @@ -249,25 +254,28 @@ public override IEnumerable GetSavedCredentials() foreach (string dir in dirs) { - string[] files = Directory.EnumerateFiles(dir, "signons.sqlite").ToArray(); - if (files.Length > 0) + if (Directory.Exists(dir)) { - signonsFile = files[0]; - signonsFound = true; - } + string[] files = Directory.EnumerateFiles(dir, "signons.sqlite").ToArray(); + if (files.Length > 0) + { + signonsFile = files[0]; + signonsFound = true; + } - // find "logins.json"file - files = Directory.EnumerateFiles(dir, "logins.json").ToArray(); - if (files.Length > 0) - { - loginsFile = files[0]; - loginsFound = true; - } + // find "logins.json"file + files = Directory.EnumerateFiles(dir, "logins.json").ToArray(); + if (files.Length > 0) + { + loginsFile = files[0]; + loginsFound = true; + } - if (loginsFound || signonsFound) - { - FFDecryptor.NSS_Init(dir); - break; + if (loginsFound || signonsFound) + { + FFDecryptor.NSS_Init(dir); + break; + } } } @@ -313,8 +321,8 @@ public override IEnumerable GetSavedCredentials() foreach (Browsers.Firefox.LoginData loginData in ffLoginData.logins) { - string username = Browsers.Firefox.FFDecryptor.Decrypt(loginData.encryptedUsername); - string password = Browsers.Firefox.FFDecryptor.Decrypt(loginData.encryptedPassword); + string username = FFDecryptor.Decrypt(loginData.encryptedUsername); + string password = FFDecryptor.Decrypt(loginData.encryptedPassword); logins.Add(new CredentialModel { Username = username, @@ -325,9 +333,9 @@ public override IEnumerable GetSavedCredentials() } } catch (Exception e) - { + { } - + return logins; } } diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/IBrowser.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/IBrowser.cs index 397e57933..add99783e 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/IBrowser.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/IBrowser.cs @@ -5,7 +5,7 @@ namespace winPEAS.KnownFileCreds.Browsers { internal interface IBrowser { - string Name { get; } + string Name { get; } void PrintInfo(); IEnumerable GetSavedCredentials(); } diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/InternetExplorer.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/InternetExplorer.cs index 5be05dd3f..af229dabc 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/InternetExplorer.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Browsers/InternetExplorer.cs @@ -1,11 +1,11 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text.RegularExpressions; -using Microsoft.Win32; using winPEAS.Checks; using winPEAS.Helpers; using winPEAS.Helpers.Registry; @@ -30,7 +30,7 @@ private static void PrintCurrentIETabs() { Beaprint.MainPrint("Current IE tabs"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history"); - List urls = InternetExplorer.GetCurrentIETabs(); + List urls = GetCurrentIETabs(); Dictionary colorsB = new Dictionary() { @@ -51,9 +51,9 @@ private static void PrintHistFavIE() { Beaprint.MainPrint("Looking for GET credentials in IE history"); Beaprint.LinkPrint("https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history"); - Dictionary> chromeHistBook = InternetExplorer.GetIEHistFav(); - List history = chromeHistBook["history"]; - List favorites = chromeHistBook["favorites"]; + Dictionary> ieHistoryBook = GetIEHistFav(); + List history = ieHistoryBook["history"]; + List favorites = ieHistoryBook["favorites"]; if (history.Count > 0) { @@ -69,8 +69,15 @@ private static void PrintHistFavIE() Beaprint.AnsiPrint(" " + url, colorsB); } } - Console.WriteLine(); + + int limit = 50; + Beaprint.MainPrint($"IE history -- limit {limit}\n"); + Beaprint.ListPrint(history.Take(limit).ToList()); + } + else + { + Beaprint.NotFoundPrint(); } Beaprint.MainPrint("IE favorites"); @@ -91,7 +98,7 @@ private static void PrintHistFavIE() { "favorites", new List() }, }; - DateTime startTime = System.DateTime.Now.AddDays(-lastDays); + DateTime startTime = DateTime.Now.AddDays(-lastDays); try { @@ -167,39 +174,31 @@ private static void PrintHistFavIE() { foreach (KeyValuePair kvp in settings) { - byte[] timeBytes = RegistryHelper.GetRegValueBytes("HKCU", "SOFTWARE\\Microsoft\\Internet Explorer\\TypedURLsTime", kvp.Key.ToString().Trim()); - if (timeBytes != null) - { - long timeLong = (long)(BitConverter.ToInt64(timeBytes, 0)); - DateTime urlTime = DateTime.FromFileTime(timeLong); - if (urlTime > startTime) - { - results["history"].Add(kvp.Value.ToString().Trim()); - } - } + results["history"].Add(kvp.Value.ToString().Trim()); } } - string userIEBookmarkPath = string.Format("{0}\\Favorites\\", System.Environment.GetEnvironmentVariable("USERPROFILE")); - - string[] bookmarkPaths = Directory.EnumerateFiles(userIEBookmarkPath, "*.url", SearchOption.AllDirectories).ToArray(); - - foreach (string bookmarkPath in bookmarkPaths) + string userIEBookmarkPath = string.Format("{0}\\Favorites\\", Environment.GetEnvironmentVariable("USERPROFILE")); + if (Directory.Exists(userIEBookmarkPath)) { - using (StreamReader rdr = new StreamReader(bookmarkPath)) + string[] bookmarkPaths = Directory.EnumerateFiles(userIEBookmarkPath, "*.url", SearchOption.AllDirectories).ToArray(); + foreach (string bookmarkPath in bookmarkPaths) { - string line; - string url = ""; - while ((line = rdr.ReadLine()) != null) + using (StreamReader rdr = new StreamReader(bookmarkPath)) { - if (line.StartsWith("URL=", StringComparison.InvariantCultureIgnoreCase)) + string line; + string url = ""; + while ((line = rdr.ReadLine()) != null) { - if (line.Length > 4) - url = line.Substring(4); - break; + if (line.StartsWith("URL=", StringComparison.InvariantCultureIgnoreCase)) + { + if (line.Length > 4) + url = line.Substring(4); + break; + } } + results["favorites"].Add(url.ToString().Trim()); } - results["favorites"].Add(url.ToString().Trim()); } } } @@ -271,7 +270,7 @@ private static List GetCurrentIETabs() public override IEnumerable GetSavedCredentials() { // unsupported - var result = new List(); + var result = new List(); return result; } } diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Enums.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Enums.cs index 36fdcd72e..f7d6a228b 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Enums.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Enums.cs @@ -1,7 +1,7 @@ using System; namespace winPEAS.KnownFileCreds.Kerberos -{ +{ public enum KERB_ENCRYPTION_TYPE : UInt32 { reserved0 = 0, diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Helpers.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Helpers.cs index 1d3905818..6a4d069e6 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Helpers.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Kerberos/Helpers.cs @@ -29,7 +29,7 @@ public static IntPtr LsaRegisterLogonProcessHelper() } catch (Exception e) { - } + } return lsaHandle; } diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/KnownFileCredsInfo.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/KnownFileCredsInfo.cs index 8f7982641..cc73d85e9 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/KnownFileCredsInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/KnownFileCredsInfo.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -6,14 +7,13 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; -using Microsoft.Win32; using winPEAS.Helpers; using winPEAS.Helpers.Registry; namespace winPEAS.KnownFileCreds { static class KnownFileCredsInfo - { + { public static Dictionary GetRecentRunCommands() { Dictionary results = new Dictionary(); @@ -34,7 +34,7 @@ static class KnownFileCredsInfo results = RegistryHelper.GetRegValues("HKCU", "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"); } return results; - } + } public static List> ListCloudCreds() { @@ -76,7 +76,7 @@ static class KnownFileCredsInfo else { var currentUserDir = Environment.GetEnvironmentVariable("USERPROFILE"); - userDirs = new List{ currentUserDir }; + userDirs = new List { currentUserDir }; } foreach (var userDir in userDirs) @@ -107,7 +107,7 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti DateTime lastModified = File.GetLastWriteTime(filePath); long size = new FileInfo(filePath).Length; - results?.Add(new Dictionary + results?.Add(new Dictionary { { "file", filePath }, { "Description", description }, @@ -123,7 +123,7 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti // parses recent file shortcuts via COM List> results = new List>(); int lastDays = 7; - DateTime startTime = System.DateTime.Now.AddDays(-lastDays); + DateTime startTime = DateTime.Now.AddDays(-lastDays); try { @@ -145,31 +145,34 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti string recentPath = string.Format("{0}\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\", dir); try { - string[] recentFiles = Directory.EnumerateFiles(recentPath, "*.lnk", SearchOption.AllDirectories).ToArray(); - - if (recentFiles.Length != 0) + if (Directory.Exists(recentPath)) { - Console.WriteLine(" {0} :\r\n", userName); - foreach (string recentFile in recentFiles) - { - DateTime lastAccessed = System.IO.File.GetLastAccessTime(recentFile); + string[] recentFiles = Directory.EnumerateFiles(recentPath, "*.lnk", SearchOption.AllDirectories).ToArray(); - if (lastAccessed > startTime) + if (recentFiles.Length != 0) + { + Console.WriteLine(" {0} :\r\n", userName); + foreach (string recentFile in recentFiles) { - // invoke the WshShell com object, creating a shortcut to then extract the TargetPath from - Object shortcut = shellObj.GetType().InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shellObj, new object[] { recentFile }); - Object TargetPath = shortcut.GetType().InvokeMember("TargetPath", BindingFlags.GetProperty, null, shortcut, new object[] { }); + DateTime lastAccessed = File.GetLastAccessTime(recentFile); - if (TargetPath.ToString().Trim() != "") + if (lastAccessed > startTime) { - results.Add(new Dictionary() + // invoke the WshShell com object, creating a shortcut to then extract the TargetPath from + Object shortcut = shellObj.GetType().InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shellObj, new object[] { recentFile }); + Object TargetPath = shortcut.GetType().InvokeMember("TargetPath", BindingFlags.GetProperty, null, shortcut, new object[] { }); + + if (TargetPath.ToString().Trim() != "") + { + results.Add(new Dictionary() { { "Target", TargetPath.ToString() }, { "Accessed", string.Format("{0}", lastAccessed) } }); + } + Marshal.ReleaseComObject(shortcut); + shortcut = null; } - Marshal.ReleaseComObject(shortcut); - shortcut = null; } } } @@ -180,33 +183,35 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti } else { - string recentPath = string.Format("{0}\\Microsoft\\Windows\\Recent\\", System.Environment.GetEnvironmentVariable("APPDATA")); - - var recentFiles = Directory.EnumerateFiles(recentPath, "*.lnk", SearchOption.AllDirectories); - - foreach (string recentFile in recentFiles) + string recentPath = string.Format("{0}\\Microsoft\\Windows\\Recent\\", Environment.GetEnvironmentVariable("APPDATA")); + if (Directory.Exists(recentPath)) { - // old method (needed interop dll) - //WshShell shell = new WshShell(); - //IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(recentFile); - - DateTime lastAccessed = System.IO.File.GetLastAccessTime(recentFile); + var recentFiles = Directory.EnumerateFiles(recentPath, "*.lnk", SearchOption.AllDirectories); - if (lastAccessed > startTime) + foreach (string recentFile in recentFiles) { - // invoke the WshShell com object, creating a shortcut to then extract the TargetPath from - Object shortcut = shellObj.GetType().InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shellObj, new object[] { recentFile }); - Object TargetPath = shortcut.GetType().InvokeMember("TargetPath", BindingFlags.GetProperty, null, shortcut, new object[] { }); - if (TargetPath.ToString().Trim() != "") + // old method (needed interop dll) + //WshShell shell = new WshShell(); + //IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(recentFile); + + DateTime lastAccessed = File.GetLastAccessTime(recentFile); + + if (lastAccessed > startTime) { - results.Add(new Dictionary() + // invoke the WshShell com object, creating a shortcut to then extract the TargetPath from + Object shortcut = shellObj.GetType().InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shellObj, new object[] { recentFile }); + Object TargetPath = shortcut.GetType().InvokeMember("TargetPath", BindingFlags.GetProperty, null, shortcut, new object[] { }); + if (TargetPath.ToString().Trim() != "") + { + results.Add(new Dictionary() { { "Target", TargetPath.ToString() }, { "Accessed", string.Format("{0}", lastAccessed) } }); + } + Marshal.ReleaseComObject(shortcut); + shortcut = null; } - Marshal.ReleaseComObject(shortcut); - shortcut = null; } } } @@ -237,13 +242,15 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti string userName = parts[parts.Length - 1]; if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { - List userDPAPIBasePaths = new List(); - userDPAPIBasePaths.Add(string.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE"))); - userDPAPIBasePaths.Add(string.Format("{0}\\AppData\\Local\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE"))); + List userDPAPIBasePaths = new List + { + string.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", Environment.GetEnvironmentVariable("USERPROFILE")), + string.Format("{0}\\AppData\\Local\\Microsoft\\Protect\\", Environment.GetEnvironmentVariable("USERPROFILE")) + }; foreach (string userDPAPIBasePath in userDPAPIBasePaths) { - if (System.IO.Directory.Exists(userDPAPIBasePath)) + if (Directory.Exists(userDPAPIBasePath)) { var directories = Directory.EnumerateDirectories(userDPAPIBasePath); foreach (string directory in directories) @@ -254,9 +261,9 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { - DateTime lastAccessed = System.IO.File.GetLastAccessTime(file); - DateTime lastModified = System.IO.File.GetLastWriteTime(file); - string fileName = System.IO.Path.GetFileName(file); + DateTime lastAccessed = File.GetLastAccessTime(file); + DateTime lastModified = File.GetLastWriteTime(file); + string fileName = Path.GetFileName(file); results.Add(new Dictionary() { { "MasterKey", file }, @@ -274,13 +281,15 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti else { string userName = Environment.GetEnvironmentVariable("USERNAME"); - List userDPAPIBasePaths = new List(); - userDPAPIBasePaths.Add(string.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE"))); - userDPAPIBasePaths.Add(string.Format("{0}\\AppData\\Local\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE"))); + List userDPAPIBasePaths = new List + { + string.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", Environment.GetEnvironmentVariable("USERPROFILE")), + string.Format("{0}\\AppData\\Local\\Microsoft\\Protect\\", Environment.GetEnvironmentVariable("USERPROFILE")) + }; - foreach (string userDPAPIBasePath in userDPAPIBasePaths) + foreach (string userDPAPIBasePath in userDPAPIBasePaths) { - if (System.IO.Directory.Exists(userDPAPIBasePath)) + if (Directory.Exists(userDPAPIBasePath)) { var directories = Directory.EnumerateDirectories(userDPAPIBasePath); foreach (string directory in directories) @@ -291,9 +300,9 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { - DateTime lastAccessed = System.IO.File.GetLastAccessTime(file); - DateTime lastModified = System.IO.File.GetLastWriteTime(file); - string fileName = System.IO.Path.GetFileName(file); + DateTime lastAccessed = File.GetLastAccessTime(file); + DateTime lastModified = File.GetLastWriteTime(file); + string fileName = Path.GetFileName(file); results.Add(new Dictionary() { { "MasterKey", file }, @@ -331,23 +340,25 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti string userName = parts[parts.Length - 1]; if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { - List userCredFilePaths = new List(); - userCredFilePaths.Add(string.Format("{0}\\AppData\\Local\\Microsoft\\Credentials\\", dir)); - userCredFilePaths.Add(string.Format("{0}\\AppData\\Roaming\\Microsoft\\Credentials\\", dir)); + List userCredFilePaths = new List + { + string.Format("{0}\\AppData\\Local\\Microsoft\\Credentials\\", dir), + string.Format("{0}\\AppData\\Roaming\\Microsoft\\Credentials\\", dir) + }; foreach (string userCredFilePath in userCredFilePaths) { - if (System.IO.Directory.Exists(userCredFilePath)) + if (Directory.Exists(userCredFilePath)) { var systemFiles = Directory.EnumerateFiles(userCredFilePath); if ((systemFiles != null)) { foreach (string file in systemFiles) { - DateTime lastAccessed = System.IO.File.GetLastAccessTime(file); - DateTime lastModified = System.IO.File.GetLastWriteTime(file); - long size = new System.IO.FileInfo(file).Length; - string fileName = System.IO.Path.GetFileName(file); + DateTime lastAccessed = File.GetLastAccessTime(file); + DateTime lastModified = File.GetLastWriteTime(file); + long size = new FileInfo(file).Length; + string fileName = Path.GetFileName(file); // jankily parse the bytes to extract the credential type and master key GUID // reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54 @@ -381,49 +392,54 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti } string systemFolder = string.Format("{0}\\System32\\config\\systemprofile\\AppData\\Local\\Microsoft\\Credentials", Environment.GetEnvironmentVariable("SystemRoot")); - var files = Directory.EnumerateFiles(systemFolder); - if ((files != null)) + if (Directory.Exists(systemFolder)) { - foreach (string file in files) + var files = Directory.EnumerateFiles(systemFolder); + if ((files != null)) { - DateTime lastAccessed = System.IO.File.GetLastAccessTime(file); - DateTime lastModified = System.IO.File.GetLastWriteTime(file); - long size = new System.IO.FileInfo(file).Length; - string fileName = System.IO.Path.GetFileName(file); - - // jankily parse the bytes to extract the credential type and master key GUID - // reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54 - byte[] credentialArray = File.ReadAllBytes(file); - byte[] guidMasterKeyArray = new byte[16]; - Array.Copy(credentialArray, 36, guidMasterKeyArray, 0, 16); - Guid guidMasterKey = new Guid(guidMasterKeyArray); - - byte[] stringLenArray = new byte[16]; - Array.Copy(credentialArray, 56, stringLenArray, 0, 4); - int descLen = BitConverter.ToInt32(stringLenArray, 0); - - byte[] descBytes = new byte[descLen]; - Array.Copy(credentialArray, 60, descBytes, 0, descLen - 4); - - string desc = Encoding.Unicode.GetString(descBytes); - results.Add(new Dictionary() + foreach (string file in files) { - { "CredFile", file }, - { "Description", desc }, - { "MasterKey", string.Format("{0}", guidMasterKey) }, - { "Accessed", string.Format("{0}", lastAccessed) }, - { "Modified", string.Format("{0}", lastModified) }, - { "Size", string.Format("{0}", size) }, - }); + DateTime lastAccessed = File.GetLastAccessTime(file); + DateTime lastModified = File.GetLastWriteTime(file); + long size = new System.IO.FileInfo(file).Length; + string fileName = Path.GetFileName(file); + + // jankily parse the bytes to extract the credential type and master key GUID + // reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54 + byte[] credentialArray = File.ReadAllBytes(file); + byte[] guidMasterKeyArray = new byte[16]; + Array.Copy(credentialArray, 36, guidMasterKeyArray, 0, 16); + Guid guidMasterKey = new Guid(guidMasterKeyArray); + + byte[] stringLenArray = new byte[16]; + Array.Copy(credentialArray, 56, stringLenArray, 0, 4); + int descLen = BitConverter.ToInt32(stringLenArray, 0); + + byte[] descBytes = new byte[descLen]; + Array.Copy(credentialArray, 60, descBytes, 0, descLen - 4); + + string desc = Encoding.Unicode.GetString(descBytes); + results.Add(new Dictionary() + { + { "CredFile", file }, + { "Description", desc }, + { "MasterKey", string.Format("{0}", guidMasterKey) }, + { "Accessed", string.Format("{0}", lastAccessed) }, + { "Modified", string.Format("{0}", lastModified) }, + { "Size", string.Format("{0}", size) }, + }); + } } } } else { string userName = Environment.GetEnvironmentVariable("USERNAME"); - List userCredFilePaths = new List(); - userCredFilePaths.Add(string.Format("{0}\\AppData\\Local\\Microsoft\\Credentials\\", System.Environment.GetEnvironmentVariable("USERPROFILE"))); - userCredFilePaths.Add(string.Format("{0}\\AppData\\Roaming\\Microsoft\\Credentials\\", System.Environment.GetEnvironmentVariable("USERPROFILE"))); + List userCredFilePaths = new List + { + string.Format("{0}\\AppData\\Local\\Microsoft\\Credentials\\", Environment.GetEnvironmentVariable("USERPROFILE")), + string.Format("{0}\\AppData\\Roaming\\Microsoft\\Credentials\\", Environment.GetEnvironmentVariable("USERPROFILE")) + }; foreach (string userCredFilePath in userCredFilePaths) { @@ -433,10 +449,10 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti foreach (string file in files) { - DateTime lastAccessed = System.IO.File.GetLastAccessTime(file); - DateTime lastModified = System.IO.File.GetLastWriteTime(file); + DateTime lastAccessed = File.GetLastAccessTime(file); + DateTime lastModified = File.GetLastWriteTime(file); long size = new System.IO.FileInfo(file).Length; - string fileName = System.IO.Path.GetFileName(file); + string fileName = Path.GetFileName(file); // jankily parse the bytes to extract the credential type and master key GUID // reference- https://github.com/gentilkiwi/mimikatz/blob/3d8be22fff9f7222f9590aa007629e18300cf643/modules/kull_m_dpapi.h#L24-L54 @@ -472,6 +488,6 @@ private static void AddCloudCredentialFromFile(string filePath, string descripti Beaprint.PrintException(ex.Message); } return results; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Putty.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Putty.cs index 14ce14139..8ddbc18a7 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Putty.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Putty.cs @@ -1,6 +1,6 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; -using Microsoft.Win32; using winPEAS.Helpers; using winPEAS.Helpers.Registry; @@ -20,7 +20,7 @@ private static void PrintPuttySess() try { Beaprint.MainPrint("Putty Sessions"); - List> putty_sess = Putty.GetPuttySessions(); + List> putty_sess = GetPuttySessions(); Dictionary colorF = new Dictionary() { @@ -39,7 +39,7 @@ private static void PrintPuttySSH() try { Beaprint.MainPrint("Putty SSH Host keys"); - List> putty_sess = Putty.ListPuttySSHHostKeys(); + List> putty_sess = ListPuttySSHHostKeys(); Dictionary colorF = new Dictionary() { { ".*", Beaprint.ansi_color_bad }, @@ -182,8 +182,10 @@ private static void PrintSSHKeysReg() Dictionary hostKeys = RegistryHelper.GetRegValues("HKU", string.Format("{0}\\Software\\SimonTatham\\PuTTY\\SshHostKeys\\", SID)); if ((hostKeys != null) && (hostKeys.Count != 0)) { - Dictionary putty_ssh = new Dictionary(); - putty_ssh["UserSID"] = SID; + Dictionary putty_ssh = new Dictionary + { + ["UserSID"] = SID + }; foreach (KeyValuePair kvp in hostKeys) { putty_ssh[kvp.Key] = ""; //Looks like only matters the key name, not the value diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/RemoteDesktop.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/RemoteDesktop.cs index 6ccb4293a..6a30c6996 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/RemoteDesktop.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/RemoteDesktop.cs @@ -1,8 +1,8 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.IO; using System.Xml; -using Microsoft.Win32; using winPEAS.Helpers; using winPEAS.Helpers.Registry; @@ -77,7 +77,7 @@ static class RemoteDesktop if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { string userRDManFile = string.Format("{0}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings", dir); - if (System.IO.File.Exists(userRDManFile)) + if (File.Exists(userRDManFile)) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(userRDManFile); @@ -87,8 +87,8 @@ static class RemoteDesktop XmlNodeList items = filesToOpen[0].ChildNodes; XmlNode node = items[0]; - DateTime lastAccessed = System.IO.File.GetLastAccessTime(userRDManFile); - DateTime lastModified = System.IO.File.GetLastWriteTime(userRDManFile); + DateTime lastAccessed = File.GetLastAccessTime(userRDManFile); + DateTime lastModified = File.GetLastWriteTime(userRDManFile); Dictionary rdg = new Dictionary(){ { "RDCManFile", userRDManFile }, { "Accessed", string.Format("{0}", lastAccessed) }, @@ -107,9 +107,9 @@ static class RemoteDesktop else { string userName = Environment.GetEnvironmentVariable("USERNAME"); - string userRDManFile = string.Format("{0}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings", System.Environment.GetEnvironmentVariable("USERPROFILE")); + string userRDManFile = string.Format("{0}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings", Environment.GetEnvironmentVariable("USERPROFILE")); - if (System.IO.File.Exists(userRDManFile)) + if (File.Exists(userRDManFile)) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(userRDManFile); @@ -119,8 +119,8 @@ static class RemoteDesktop XmlNodeList items = filesToOpen[0].ChildNodes; XmlNode node = items[0]; - DateTime lastAccessed = System.IO.File.GetLastAccessTime(userRDManFile); - DateTime lastModified = System.IO.File.GetLastWriteTime(userRDManFile); + DateTime lastAccessed = File.GetLastAccessTime(userRDManFile); + DateTime lastModified = File.GetLastWriteTime(userRDManFile); Dictionary rdg = new Dictionary(){ { "RDCManFile", userRDManFile }, { "Accessed", string.Format("{0}", lastAccessed) }, diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/NtlmHashInfo.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/NtlmHashInfo.cs index 2699b00c3..dbae92112 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/NtlmHashInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/NtlmHashInfo.cs @@ -9,6 +9,6 @@ public NtlmHashInfo(string version, string hash) { Version = version; Hash = hash; - } + } } } diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecBuffer.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecBuffer.cs index af152d147..29ac5dd7c 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecBuffer.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecBuffer.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; namespace winPEAS.KnownFileCreds.SecurityPackages -{ +{ [StructLayout(LayoutKind.Sequential)] public struct SecBuffer : IDisposable { diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecurityPackages.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecurityPackages.cs index 85b055de3..b46d2ad12 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecurityPackages.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SecurityPackages/SecurityPackages.cs @@ -8,7 +8,7 @@ namespace winPEAS.KnownFileCreds.SecurityPackages { internal class SecurityPackages - { + { [StructLayout(LayoutKind.Sequential)] public struct SECURITY_INTEGER { @@ -30,7 +30,7 @@ internal static IEnumerable GetNtlmCredentials() if (cred != null) { yield return cred; - } + } } private static NtlmHashInfo GetNtlmCredentialsInternal(string challenge, bool disableESS) @@ -142,7 +142,7 @@ out clientLifeTime return ParseNTResponse(clientTokenBytes, challenge); } else if (result == SEC_E_NO_CREDENTIALS) - { + { return null; } else if (disableESS) @@ -209,7 +209,7 @@ private static NtlmHashInfo ParseNTResponse(byte[] message, string challenge) { return new NtlmHashInfo( "NetNTLMv2", - FormatNetNtlmV2Hash(challenge, user, domain, SubArray(nt_resp, 0, 16), SubArray(nt_resp,16, nt_resp.Length - 16)) + FormatNetNtlmV2Hash(challenge, user, domain, SubArray(nt_resp, 0, 16), SubArray(nt_resp, 16, nt_resp.Length - 16)) ); } else @@ -253,7 +253,7 @@ private static byte[] StringToByteArray(string hexString) private static string ByteArrayToString(byte[] ba) { var hex = new StringBuilder(ba.Length * 2); - + foreach (var b in ba) { hex.AppendFormat("{0:x2}", b); diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SuperPutty/SuperPutty.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SuperPutty/SuperPutty.cs index 29f646f1f..3e7172b5c 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SuperPutty/SuperPutty.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/SuperPutty/SuperPutty.cs @@ -15,7 +15,7 @@ public static void PrintInfo() private static void PrintConfigurationFiles() { Beaprint.MainPrint("SuperPutty configuration files"); - + var dirs = User.GetUsersFolders(); var filter = "sessions*.xml"; @@ -24,11 +24,14 @@ private static void PrintConfigurationFiles() try { var path = $"{dir}\\Documents\\SuperPuTTY\\"; - var files = Directory.EnumerateFiles(path, filter, SearchOption.TopDirectoryOnly); - - foreach (var file in files) + if (Directory.Exists(path)) { - Beaprint.BadPrint($" {file}"); + var files = Directory.EnumerateFiles(path, filter, SearchOption.TopDirectoryOnly); + + foreach (var file in files) + { + Beaprint.BadPrint($" {file}"); + } } } catch (Exception) diff --git a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Vault/VaultCli.cs b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Vault/VaultCli.cs index 02f89e469..509938ecb 100644 --- a/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Vault/VaultCli.cs +++ b/winPEAS/winPEASexe/winPEAS/KnownFileCreds/Vault/VaultCli.cs @@ -45,16 +45,18 @@ public static class VaultCli // Create dictionary to translate Guids to human readable elements IntPtr guidAddress = vaultGuidPtr; - Dictionary vaultSchema = new Dictionary(); - vaultSchema.Add(new Guid("2F1A6504-0641-44CF-8BB5-3612D865F2E5"), "Windows Secure Note"); - vaultSchema.Add(new Guid("3CCD5499-87A8-4B10-A215-608888DD3B55"), "Windows Web Password Credential"); - vaultSchema.Add(new Guid("154E23D0-C644-4E6F-8CE6-5069272F999F"), "Windows Credential Picker Protector"); - vaultSchema.Add(new Guid("4BF4C442-9B8A-41A0-B380-DD4A704DDB28"), "Web Credentials"); - vaultSchema.Add(new Guid("77BC582B-F0A6-4E15-4E80-61736B6F3B29"), "Windows Credentials"); - vaultSchema.Add(new Guid("E69D7838-91B5-4FC9-89D5-230D4D4CC2BC"), "Windows Domain Certificate Credential"); - vaultSchema.Add(new Guid("3E0E35BE-1B77-43E7-B873-AED901B6275B"), "Windows Domain Password Credential"); - vaultSchema.Add(new Guid("3C886FF3-2669-4AA2-A8FB-3F6759A77548"), "Windows Extended Credential"); - vaultSchema.Add(new Guid("00000000-0000-0000-0000-000000000000"), null); + Dictionary vaultSchema = new Dictionary + { + { new Guid("2F1A6504-0641-44CF-8BB5-3612D865F2E5"), "Windows Secure Note" }, + { new Guid("3CCD5499-87A8-4B10-A215-608888DD3B55"), "Windows Web Password Credential" }, + { new Guid("154E23D0-C644-4E6F-8CE6-5069272F999F"), "Windows Credential Picker Protector" }, + { new Guid("4BF4C442-9B8A-41A0-B380-DD4A704DDB28"), "Web Credentials" }, + { new Guid("77BC582B-F0A6-4E15-4E80-61736B6F3B29"), "Windows Credentials" }, + { new Guid("E69D7838-91B5-4FC9-89D5-230D4D4CC2BC"), "Windows Domain Certificate Credential" }, + { new Guid("3E0E35BE-1B77-43E7-B873-AED901B6275B"), "Windows Domain Password Credential" }, + { new Guid("3C886FF3-2669-4AA2-A8FB-3F6759A77548"), "Windows Extended Credential" }, + { new Guid("00000000-0000-0000-0000-000000000000"), null } + }; for (int i = 0; i < vaultCount; i++) { @@ -167,7 +169,7 @@ public static class VaultCli vault_cred["PacakgeSid"] = string.Format("{0}", packageSid); } vault_cred["Credential"] = string.Format("{0}", cred); - vault_cred["Last Modified"] = string.Format("{0}", System.DateTime.FromFileTimeUtc((long)lastModified)); + vault_cred["Last Modified"] = string.Format("{0}", DateTime.FromFileTimeUtc((long)lastModified)); results.Add(vault_cred); } } diff --git a/winPEAS/winPEASexe/winPEAS/Native/Classes/SafeTokenHandle.cs b/winPEAS/winPEASexe/winPEAS/Native/Classes/SafeTokenHandle.cs index fabddbd67..12ff978aa 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Classes/SafeTokenHandle.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Classes/SafeTokenHandle.cs @@ -1,15 +1,14 @@ using System; using System.Runtime.InteropServices; using winPEAS.Native.Enums; -using winPEAS.TaskScheduler.TaskEditor.Native; namespace winPEAS.Native.Classes { - public partial class SafeTokenHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid - { - private const Int32 ERROR_NO_TOKEN = 0x000003F0; - private const Int32 ERROR_INSUFFICIENT_BUFFER = 122; - private static SafeTokenHandle currentProcessToken = null; + public partial class SafeTokenHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid + { + private const Int32 ERROR_NO_TOKEN = 0x000003F0; + private const Int32 ERROR_INSUFFICIENT_BUFFER = 122; + private static SafeTokenHandle currentProcessToken = null; private SafeTokenHandle() : base(true) { } @@ -20,102 +19,102 @@ internal SafeTokenHandle(IntPtr handle, bool own = true) : base(own) protected override bool ReleaseHandle() => Kernel32.CloseHandle(handle); - public T GetInfo(TOKEN_INFORMATION_CLASS type) - { - int cbSize = Marshal.SizeOf(typeof(T)); - IntPtr pType = Marshal.AllocHGlobal(cbSize); + public T GetInfo(TOKEN_INFORMATION_CLASS type) + { + int cbSize = Marshal.SizeOf(typeof(T)); + IntPtr pType = Marshal.AllocHGlobal(cbSize); - try - { - // Retrieve token information. - if (!Advapi32.GetTokenInformation(this, type, pType, cbSize, out cbSize)) - throw new System.ComponentModel.Win32Exception(); + try + { + // Retrieve token information. + if (!Advapi32.GetTokenInformation(this, type, pType, cbSize, out cbSize)) + throw new System.ComponentModel.Win32Exception(); - // Marshal from native to .NET. - switch (type) - { - case TOKEN_INFORMATION_CLASS.TokenType: - case TOKEN_INFORMATION_CLASS.TokenImpersonationLevel: - case TOKEN_INFORMATION_CLASS.TokenSessionId: - case TOKEN_INFORMATION_CLASS.TokenSandBoxInert: - case TOKEN_INFORMATION_CLASS.TokenOrigin: - case TOKEN_INFORMATION_CLASS.TokenElevationType: - case TOKEN_INFORMATION_CLASS.TokenHasRestrictions: - case TOKEN_INFORMATION_CLASS.TokenUIAccess: - case TOKEN_INFORMATION_CLASS.TokenVirtualizationAllowed: - case TOKEN_INFORMATION_CLASS.TokenVirtualizationEnabled: - return (T)Convert.ChangeType(Marshal.ReadInt32(pType), typeof(T)); + // Marshal from native to .NET. + switch (type) + { + case TOKEN_INFORMATION_CLASS.TokenType: + case TOKEN_INFORMATION_CLASS.TokenImpersonationLevel: + case TOKEN_INFORMATION_CLASS.TokenSessionId: + case TOKEN_INFORMATION_CLASS.TokenSandBoxInert: + case TOKEN_INFORMATION_CLASS.TokenOrigin: + case TOKEN_INFORMATION_CLASS.TokenElevationType: + case TOKEN_INFORMATION_CLASS.TokenHasRestrictions: + case TOKEN_INFORMATION_CLASS.TokenUIAccess: + case TOKEN_INFORMATION_CLASS.TokenVirtualizationAllowed: + case TOKEN_INFORMATION_CLASS.TokenVirtualizationEnabled: + return (T)Convert.ChangeType(Marshal.ReadInt32(pType), typeof(T)); - case TOKEN_INFORMATION_CLASS.TokenLinkedToken: - return (T)Convert.ChangeType(Marshal.ReadIntPtr(pType), typeof(T)); + case TOKEN_INFORMATION_CLASS.TokenLinkedToken: + return (T)Convert.ChangeType(Marshal.ReadIntPtr(pType), typeof(T)); - case TOKEN_INFORMATION_CLASS.TokenUser: - case TOKEN_INFORMATION_CLASS.TokenGroups: - case TOKEN_INFORMATION_CLASS.TokenPrivileges: - case TOKEN_INFORMATION_CLASS.TokenOwner: - case TOKEN_INFORMATION_CLASS.TokenPrimaryGroup: - case TOKEN_INFORMATION_CLASS.TokenDefaultDacl: - case TOKEN_INFORMATION_CLASS.TokenSource: - case TOKEN_INFORMATION_CLASS.TokenStatistics: - case TOKEN_INFORMATION_CLASS.TokenRestrictedSids: - case TOKEN_INFORMATION_CLASS.TokenGroupsAndPrivileges: - case TOKEN_INFORMATION_CLASS.TokenElevation: - case TOKEN_INFORMATION_CLASS.TokenAccessInformation: - case TOKEN_INFORMATION_CLASS.TokenIntegrityLevel: - case TOKEN_INFORMATION_CLASS.TokenMandatoryPolicy: - case TOKEN_INFORMATION_CLASS.TokenLogonSid: - return (T)Marshal.PtrToStructure(pType, typeof(T)); + case TOKEN_INFORMATION_CLASS.TokenUser: + case TOKEN_INFORMATION_CLASS.TokenGroups: + case TOKEN_INFORMATION_CLASS.TokenPrivileges: + case TOKEN_INFORMATION_CLASS.TokenOwner: + case TOKEN_INFORMATION_CLASS.TokenPrimaryGroup: + case TOKEN_INFORMATION_CLASS.TokenDefaultDacl: + case TOKEN_INFORMATION_CLASS.TokenSource: + case TOKEN_INFORMATION_CLASS.TokenStatistics: + case TOKEN_INFORMATION_CLASS.TokenRestrictedSids: + case TOKEN_INFORMATION_CLASS.TokenGroupsAndPrivileges: + case TOKEN_INFORMATION_CLASS.TokenElevation: + case TOKEN_INFORMATION_CLASS.TokenAccessInformation: + case TOKEN_INFORMATION_CLASS.TokenIntegrityLevel: + case TOKEN_INFORMATION_CLASS.TokenMandatoryPolicy: + case TOKEN_INFORMATION_CLASS.TokenLogonSid: + return (T)Marshal.PtrToStructure(pType, typeof(T)); - case TOKEN_INFORMATION_CLASS.TokenSessionReference: - case TOKEN_INFORMATION_CLASS.TokenAuditPolicy: - default: - return default(T); - } - } - finally - { - Marshal.FreeHGlobal(pType); - } - } + case TOKEN_INFORMATION_CLASS.TokenSessionReference: + case TOKEN_INFORMATION_CLASS.TokenAuditPolicy: + default: + return default(T); + } + } + finally + { + Marshal.FreeHGlobal(pType); + } + } - public static SafeTokenHandle FromCurrentProcess(AccessTypes desiredAccess = AccessTypes.TokenDuplicate) - { - lock (currentProcessToken) - { - if (currentProcessToken == null) - currentProcessToken = FromProcess(Kernel32.GetCurrentProcess(), desiredAccess); - return currentProcessToken; - } - } + public static SafeTokenHandle FromCurrentProcess(AccessTypes desiredAccess = AccessTypes.TokenDuplicate) + { + lock (currentProcessToken) + { + if (currentProcessToken == null) + currentProcessToken = FromProcess(Kernel32.GetCurrentProcess(), desiredAccess); + return currentProcessToken; + } + } - public static SafeTokenHandle FromCurrentThread(AccessTypes desiredAccess = AccessTypes.TokenDuplicate, bool openAsSelf = true) + public static SafeTokenHandle FromCurrentThread(AccessTypes desiredAccess = AccessTypes.TokenDuplicate, bool openAsSelf = true) => FromThread(Kernel32.GetCurrentThread(), desiredAccess, openAsSelf); - public static SafeTokenHandle FromProcess(IntPtr hProcess, AccessTypes desiredAccess = AccessTypes.TokenDuplicate) - { - SafeTokenHandle val; - if (!Advapi32.OpenProcessToken(hProcess, desiredAccess, out val)) - throw new System.ComponentModel.Win32Exception(); - return val; - } + public static SafeTokenHandle FromProcess(IntPtr hProcess, AccessTypes desiredAccess = AccessTypes.TokenDuplicate) + { + SafeTokenHandle val; + if (!Advapi32.OpenProcessToken(hProcess, desiredAccess, out val)) + throw new System.ComponentModel.Win32Exception(); + return val; + } - public static SafeTokenHandle FromThread(IntPtr hThread, AccessTypes desiredAccess = AccessTypes.TokenDuplicate, bool openAsSelf = true) - { - SafeTokenHandle val; - if (!Advapi32.OpenThreadToken(hThread, desiredAccess, openAsSelf, out val)) - { - if (Marshal.GetLastWin32Error() == ERROR_NO_TOKEN) - { - SafeTokenHandle pval = FromCurrentProcess(); - if (!Advapi32.DuplicateTokenEx(pval, AccessTypes.TokenImpersonate | desiredAccess, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.Impersonation, TokenType.TokenImpersonation, ref val)) - throw new System.ComponentModel.Win32Exception(); - if (!Advapi32.SetThreadToken(IntPtr.Zero, val)) - throw new System.ComponentModel.Win32Exception(); - } - else - throw new System.ComponentModel.Win32Exception(); - } - return val; - } - } + public static SafeTokenHandle FromThread(IntPtr hThread, AccessTypes desiredAccess = AccessTypes.TokenDuplicate, bool openAsSelf = true) + { + SafeTokenHandle val; + if (!Advapi32.OpenThreadToken(hThread, desiredAccess, openAsSelf, out val)) + { + if (Marshal.GetLastWin32Error() == ERROR_NO_TOKEN) + { + SafeTokenHandle pval = FromCurrentProcess(); + if (!Advapi32.DuplicateTokenEx(pval, AccessTypes.TokenImpersonate | desiredAccess, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.Impersonation, TokenType.TokenImpersonation, ref val)) + throw new System.ComponentModel.Win32Exception(); + if (!Advapi32.SetThreadToken(IntPtr.Zero, val)) + throw new System.ComponentModel.Win32Exception(); + } + else + throw new System.ComponentModel.Win32Exception(); + } + return val; + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/Native/Enums/DS_NAME_FORMAT.cs b/winPEAS/winPEASexe/winPEAS/Native/Enums/DS_NAME_FORMAT.cs index 621ebbf3c..98846ad99 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Enums/DS_NAME_FORMAT.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Enums/DS_NAME_FORMAT.cs @@ -1,38 +1,38 @@ namespace winPEAS.Native.Enums { - /// - /// Provides formats to use for input and output names for the DsCrackNames function. - /// - public enum DS_NAME_FORMAT - { - ///Indicates the name is using an unknown name type. This format can impact performance because it forces the server to attempt to match all possible formats. Only use this value if the input format is unknown. - DS_UNKNOWN_NAME = 0, + /// + /// Provides formats to use for input and output names for the DsCrackNames function. + /// + public enum DS_NAME_FORMAT + { + ///Indicates the name is using an unknown name type. This format can impact performance because it forces the server to attempt to match all possible formats. Only use this value if the input format is unknown. + DS_UNKNOWN_NAME = 0, - ///Indicates that the fully qualified distinguished name is used. For example: "CN = someone, OU = Users, DC = Engineering, DC = Fabrikam, DC = Com" - DS_FQDN_1779_NAME = 1, + ///Indicates that the fully qualified distinguished name is used. For example: "CN = someone, OU = Users, DC = Engineering, DC = Fabrikam, DC = Com" + DS_FQDN_1779_NAME = 1, - ///Indicates a Windows NT 4.0 account name. For example: "Engineering\someone" The domain-only version includes two trailing backslashes (\\). - DS_NT4_ACCOUNT_NAME = 2, + ///Indicates a Windows NT 4.0 account name. For example: "Engineering\someone" The domain-only version includes two trailing backslashes (\\). + DS_NT4_ACCOUNT_NAME = 2, - ///Indicates a user-friendly display name, for example, Jeff Smith. The display name is not necessarily the same as relative distinguished name (RDN). - DS_DISPLAY_NAME = 3, + ///Indicates a user-friendly display name, for example, Jeff Smith. The display name is not necessarily the same as relative distinguished name (RDN). + DS_DISPLAY_NAME = 3, - ///Indicates a GUID string that the IIDFromString function returns. For example: "{4fa050f0-f561-11cf-bdd9-00aa003a77b6}" - DS_UNIQUE_ID_NAME = 6, + ///Indicates a GUID string that the IIDFromString function returns. For example: "{4fa050f0-f561-11cf-bdd9-00aa003a77b6}" + DS_UNIQUE_ID_NAME = 6, - ///Indicates a complete canonical name. For example: "engineering.fabrikam.com/software/someone" The domain-only version includes a trailing forward slash (/). - DS_CANONICAL_NAME = 7, + ///Indicates a complete canonical name. For example: "engineering.fabrikam.com/software/someone" The domain-only version includes a trailing forward slash (/). + DS_CANONICAL_NAME = 7, - ///Indicates that it is using the user principal name (UPN). For example: "someone@engineering.fabrikam.com" - DS_USER_PRINCIPAL_NAME = 8, + ///Indicates that it is using the user principal name (UPN). For example: "someone@engineering.fabrikam.com" + DS_USER_PRINCIPAL_NAME = 8, - ///This element is the same as DS_CANONICAL_NAME except that the rightmost forward slash (/) is replaced with a newline character (\n), even in a domain-only case. For example: "engineering.fabrikam.com/software\nsomeone" - DS_CANONICAL_NAME_EX = 9, + ///This element is the same as DS_CANONICAL_NAME except that the rightmost forward slash (/) is replaced with a newline character (\n), even in a domain-only case. For example: "engineering.fabrikam.com/software\nsomeone" + DS_CANONICAL_NAME_EX = 9, - ///Indicates it is using a generalized service principal name. For example: "www/www.fabrikam.com@fabrikam.com" - DS_SERVICE_PRINCIPAL_NAME = 10, + ///Indicates it is using a generalized service principal name. For example: "www/www.fabrikam.com@fabrikam.com" + DS_SERVICE_PRINCIPAL_NAME = 10, - ///Indicates a Security Identifier (SID) for the object. This can be either the current SID or a SID from the object SID history. The SID string can use either the standard string representation of a SID, or one of the string constants defined in Sddl.h. For more information about converting a binary SID into a SID string, see SID Strings. The following is an example of a SID string: "S-1-5-21-397955417-626881126-188441444-501" - DS_SID_OR_SID_HISTORY_NAME = 11, - } + ///Indicates a Security Identifier (SID) for the object. This can be either the current SID or a SID from the object SID history. The SID string can use either the standard string representation of a SID, or one of the string constants defined in Sddl.h. For more information about converting a binary SID into a SID string, see SID Strings. The following is an example of a SID string: "S-1-5-21-397955417-626881126-188441444-501" + DS_SID_OR_SID_HISTORY_NAME = 11, + } } diff --git a/winPEAS/winPEASexe/winPEAS/Native/Enums/SECURITY_IMPERSONATION_LEVEL.cs b/winPEAS/winPEASexe/winPEAS/Native/Enums/SECURITY_IMPERSONATION_LEVEL.cs index 63e628a2f..b9fd05ed5 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Enums/SECURITY_IMPERSONATION_LEVEL.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Enums/SECURITY_IMPERSONATION_LEVEL.cs @@ -1,6 +1,6 @@ namespace winPEAS.Native.Enums { - public enum SECURITY_IMPERSONATION_LEVEL + public enum SECURITY_IMPERSONATION_LEVEL { Anonymous, Identification, diff --git a/winPEAS/winPEASexe/winPEAS/Native/Enums/ServerTypes.cs b/winPEAS/winPEASexe/winPEAS/Native/Enums/ServerTypes.cs index 09aa88384..6a49c4c2c 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Enums/ServerTypes.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Enums/ServerTypes.cs @@ -2,7 +2,7 @@ namespace winPEAS.Native.Enums { - [Flags] + [Flags] public enum ServerTypes : uint { Workstation = 0x00000001, diff --git a/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_ELEVATION_TYPE.cs b/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_ELEVATION_TYPE.cs index 235c43629..d75405e68 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_ELEVATION_TYPE.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_ELEVATION_TYPE.cs @@ -1,6 +1,6 @@ namespace winPEAS.Native.Enums { - public enum TOKEN_ELEVATION_TYPE + public enum TOKEN_ELEVATION_TYPE { Default = 1, Full, diff --git a/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_INFORMATION_CLASS.cs b/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_INFORMATION_CLASS.cs index 93c1a15c4..ac9977ed5 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_INFORMATION_CLASS.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Enums/TOKEN_INFORMATION_CLASS.cs @@ -1,6 +1,6 @@ namespace winPEAS.Native.Enums { - public enum TOKEN_INFORMATION_CLASS + public enum TOKEN_INFORMATION_CLASS { TokenUser = 1, TokenGroups, diff --git a/winPEAS/winPEASexe/winPEAS/Native/Ntdll.cs b/winPEAS/winPEASexe/winPEAS/Native/Ntdll.cs index 95523a531..1a5bc0b99 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Ntdll.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Ntdll.cs @@ -1,7 +1,5 @@ using System; -using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; -using winPEAS.Info.SystemInfo.NamedPipes; namespace winPEAS.Native { diff --git a/winPEAS/winPEASexe/winPEAS/Native/Ntdsapi.cs b/winPEAS/winPEASexe/winPEAS/Native/Ntdsapi.cs index bc4428299..1cfd75612 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Ntdsapi.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Ntdsapi.cs @@ -27,5 +27,5 @@ internal class Ntdsapi [DllImport("ntdsapi.dll", CharSet = CharSet.Auto)] internal static extern uint DsUnBind(ref IntPtr phDS); - } + } } diff --git a/winPEAS/winPEASexe/winPEAS/Native/Structs/LUID.cs b/winPEAS/winPEASexe/winPEAS/Native/Structs/LUID.cs index 96c38c4aa..2a16da069 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Structs/LUID.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Structs/LUID.cs @@ -2,7 +2,7 @@ namespace winPEAS.Native.Structs { - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct LUID { public uint LowPart; diff --git a/winPEAS/winPEASexe/winPEAS/Native/Structs/SID_AND_ATTRIBUTES.cs b/winPEAS/winPEASexe/winPEAS/Native/Structs/SID_AND_ATTRIBUTES.cs index 02a257f99..8d0c70a35 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Structs/SID_AND_ATTRIBUTES.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Structs/SID_AND_ATTRIBUTES.cs @@ -3,7 +3,7 @@ namespace winPEAS.Native.Structs { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential)] public struct SID_AND_ATTRIBUTES { public IntPtr Sid; diff --git a/winPEAS/winPEASexe/winPEAS/Native/Structs/TOKEN_ELEVATION.cs b/winPEAS/winPEASexe/winPEAS/Native/Structs/TOKEN_ELEVATION.cs index 74707cb59..c73962cbe 100644 --- a/winPEAS/winPEASexe/winPEAS/Native/Structs/TOKEN_ELEVATION.cs +++ b/winPEAS/winPEASexe/winPEAS/Native/Structs/TOKEN_ELEVATION.cs @@ -3,7 +3,7 @@ namespace winPEAS.Native.Structs { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential)] public struct TOKEN_ELEVATION { public Int32 TokenIsElevated; diff --git a/winPEAS/winPEASexe/winPEAS/Properties/AssemblyInfo.cs b/winPEAS/winPEASexe/winPEAS/Properties/AssemblyInfo.cs index 79c58da9c..d6bbf89e3 100755 --- a/winPEAS/winPEASexe/winPEAS/Properties/AssemblyInfo.cs +++ b/winPEAS/winPEASexe/winPEAS/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/AccessControlExtension.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/AccessControlExtension.cs index a398073f0..0f2dbc519 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/AccessControlExtension.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/AccessControlExtension.cs @@ -2,93 +2,91 @@ using System.Collections.Generic; using System.Linq; using System.Security.AccessControl; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - /// Extensions for classes in the System.Security.AccessControl namespace. - public static class AccessControlExtension - { - /// Canonicalizes the specified Access Control List. - /// The Access Control List. - public static void Canonicalize(this RawAcl acl) - { - if (acl == null) throw new ArgumentNullException(nameof(acl)); + /// Extensions for classes in the System.Security.AccessControl namespace. + public static class AccessControlExtension + { + /// Canonicalizes the specified Access Control List. + /// The Access Control List. + public static void Canonicalize(this RawAcl acl) + { + if (acl == null) throw new ArgumentNullException(nameof(acl)); - // Extract aces to list - var aces = new System.Collections.Generic.List(acl.Cast()); + // Extract aces to list + var aces = new System.Collections.Generic.List(acl.Cast()); - // Sort aces based on canonical order - aces.Sort((a, b) => System.Collections.Generic.Comparer.Default.Compare(GetComparisonValue(a), GetComparisonValue(b))); + // Sort aces based on canonical order + aces.Sort((a, b) => Comparer.Default.Compare(GetComparisonValue(a), GetComparisonValue(b))); - // Add sorted aces back to ACL - while (acl.Count > 0) acl.RemoveAce(0); - var aceIndex = 0; - aces.ForEach(ace => acl.InsertAce(aceIndex++, ace)); - } + // Add sorted aces back to ACL + while (acl.Count > 0) acl.RemoveAce(0); + var aceIndex = 0; + aces.ForEach(ace => acl.InsertAce(aceIndex++, ace)); + } - /// Sort ACEs according to canonical form for this . - /// The object security whose DiscretionaryAcl will be made canonical. - public static void CanonicalizeAccessRules(this ObjectSecurity objectSecurity) - { - if (objectSecurity == null) throw new ArgumentNullException(nameof(objectSecurity)); - if (objectSecurity.AreAccessRulesCanonical) return; + /// Sort ACEs according to canonical form for this . + /// The object security whose DiscretionaryAcl will be made canonical. + public static void CanonicalizeAccessRules(this ObjectSecurity objectSecurity) + { + if (objectSecurity == null) throw new ArgumentNullException(nameof(objectSecurity)); + if (objectSecurity.AreAccessRulesCanonical) return; - // Get raw SD from objectSecurity and canonicalize DACL - var sd = new RawSecurityDescriptor(objectSecurity.GetSecurityDescriptorBinaryForm(), 0); - sd.DiscretionaryAcl.Canonicalize(); + // Get raw SD from objectSecurity and canonicalize DACL + var sd = new RawSecurityDescriptor(objectSecurity.GetSecurityDescriptorBinaryForm(), 0); + sd.DiscretionaryAcl.Canonicalize(); - // Convert SD back into objectSecurity - objectSecurity.SetSecurityDescriptorBinaryForm(sd.GetBinaryForm()); - } + // Convert SD back into objectSecurity + objectSecurity.SetSecurityDescriptorBinaryForm(sd.GetBinaryForm()); + } - /// Returns an array of byte values that represents the information contained in this object. - /// The object. - /// The byte array into which the contents of the is marshaled. - public static byte[] GetBinaryForm(this GenericSecurityDescriptor sd) - { - if (sd == null) throw new ArgumentNullException(nameof(sd)); - var bin = new byte[sd.BinaryLength]; - sd.GetBinaryForm(bin, 0); - return bin; - } + /// Returns an array of byte values that represents the information contained in this object. + /// The object. + /// The byte array into which the contents of the is marshaled. + public static byte[] GetBinaryForm(this GenericSecurityDescriptor sd) + { + if (sd == null) throw new ArgumentNullException(nameof(sd)); + var bin = new byte[sd.BinaryLength]; + sd.GetBinaryForm(bin, 0); + return bin; + } - // A canonical ACL must have ACES sorted according to the following order: - // 1. Access-denied on the object - // 2. Access-denied on a child or property - // 3. Access-allowed on the object - // 4. Access-allowed on a child or property - // 5. All inherited ACEs - private static byte GetComparisonValue(GenericAce ace) - { - if ((ace.AceFlags & AceFlags.Inherited) != 0) - return 5; - switch (ace.AceType) - { - case AceType.AccessDenied: - case AceType.AccessDeniedCallback: - case AceType.SystemAudit: - case AceType.SystemAlarm: - case AceType.SystemAuditCallback: - case AceType.SystemAlarmCallback: - return 0; - case AceType.AccessDeniedObject: - case AceType.AccessDeniedCallbackObject: - case AceType.SystemAuditObject: - case AceType.SystemAlarmObject: - case AceType.SystemAuditCallbackObject: - case AceType.SystemAlarmCallbackObject: - return 1; - case AceType.AccessAllowed: - case AceType.AccessAllowedCallback: - return 2; - case AceType.AccessAllowedObject: - case AceType.AccessAllowedCallbackObject: - return 3; - default: - return 4; - } - } - } + // A canonical ACL must have ACES sorted according to the following order: + // 1. Access-denied on the object + // 2. Access-denied on a child or property + // 3. Access-allowed on the object + // 4. Access-allowed on a child or property + // 5. All inherited ACEs + private static byte GetComparisonValue(GenericAce ace) + { + if ((ace.AceFlags & AceFlags.Inherited) != 0) + return 5; + switch (ace.AceType) + { + case AceType.AccessDenied: + case AceType.AccessDeniedCallback: + case AceType.SystemAudit: + case AceType.SystemAlarm: + case AceType.SystemAuditCallback: + case AceType.SystemAlarmCallback: + return 0; + case AceType.AccessDeniedObject: + case AceType.AccessDeniedCallbackObject: + case AceType.SystemAuditObject: + case AceType.SystemAlarmObject: + case AceType.SystemAuditCallbackObject: + case AceType.SystemAlarmCallbackObject: + return 1; + case AceType.AccessAllowed: + case AceType.AccessAllowedCallback: + return 2; + case AceType.AccessAllowedObject: + case AceType.AccessAllowedCallbackObject: + return 3; + default: + return 4; + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Action.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Action.cs index 5cd05fedc..efb35d838 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Action.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Action.cs @@ -1,10 +1,10 @@ -using System; +using Microsoft.Win32; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Runtime.InteropServices; using System.Xml.Serialization; -using Microsoft.Win32; using winPEAS.TaskScheduler.V1; using winPEAS.TaskScheduler.V2; @@ -252,8 +252,8 @@ internal virtual void Bind(ITaskDefinition iTaskDef) [NotNull] private static Type GetObjectType(TaskActionType actionType) { - switch (actionType) - { + switch (actionType) + { case TaskActionType.ComHandler: return typeof(ComHandlerAction); case TaskActionType.SendEmail: @@ -263,780 +263,780 @@ private static Type GetObjectType(TaskActionType actionType) default: return typeof(ExecAction); - } + } } - /// - /// Represents an action that fires a handler. Only available on Task Scheduler 2.0. Only available for Task Scheduler 2.0 on - /// Windows Vista or Windows Server 2003 and later. - /// - /// - /// This action is the most complex. It allows the task to execute and In-Proc COM server object that implements the ITaskHandler - /// interface. There is a sample project that shows how to do this in the Downloads section. - /// - /// - /// - /// - /// - /// - [XmlType(IncludeInSchema = true)] - [XmlRoot("ComHandler", Namespace = TaskDefinition.tns, IsNullable = false)] - public class ComHandlerAction : Action, IBindAsExecAction - { - /// Creates an unbound instance of . - public ComHandlerAction() { } - - /// Creates an unbound instance of . - /// Identifier of the handler class. - /// Addition data associated with the handler. - public ComHandlerAction(Guid classId, [CanBeNull] string data) - { - ClassId = classId; - Data = data; - } + /// + /// Represents an action that fires a handler. Only available on Task Scheduler 2.0. Only available for Task Scheduler 2.0 on + /// Windows Vista or Windows Server 2003 and later. + /// + /// + /// This action is the most complex. It allows the task to execute and In-Proc COM server object that implements the ITaskHandler + /// interface. There is a sample project that shows how to do this in the Downloads section. + /// + /// + /// + /// + /// + /// + [XmlType(IncludeInSchema = true)] + [XmlRoot("ComHandler", Namespace = TaskDefinition.tns, IsNullable = false)] + public class ComHandlerAction : Action, IBindAsExecAction + { + /// Creates an unbound instance of . + public ComHandlerAction() { } + + /// Creates an unbound instance of . + /// Identifier of the handler class. + /// Addition data associated with the handler. + public ComHandlerAction(Guid classId, [CanBeNull] string data) + { + ClassId = classId; + Data = data; + } - internal ComHandlerAction([NotNull] ITask task) : base(task) - { - } + internal ComHandlerAction([NotNull] ITask task) : base(task) + { + } - internal ComHandlerAction([NotNull] IAction action) : base(action) - { - } + internal ComHandlerAction([NotNull] IAction action) : base(action) + { + } - /// Gets or sets the identifier of the handler class. - public Guid ClassId - { - get => new Guid(GetProperty(nameof(ClassId), Guid.Empty.ToString())); - set => SetProperty(nameof(ClassId), value.ToString()); - } + /// Gets or sets the identifier of the handler class. + public Guid ClassId + { + get => new Guid(GetProperty(nameof(ClassId), Guid.Empty.ToString())); + set => SetProperty(nameof(ClassId), value.ToString()); + } - /// Gets the name of the object referred to by . - public string ClassName => GetNameForCLSID(ClassId); + /// Gets the name of the object referred to by . + public string ClassName => GetNameForCLSID(ClassId); - /// Gets or sets additional data that is associated with the handler. - [DefaultValue(null)] - [CanBeNull] - public string Data - { - get => GetProperty(nameof(Data)); - set => SetProperty(nameof(Data), value); - } + /// Gets or sets additional data that is associated with the handler. + [DefaultValue(null)] + [CanBeNull] + public string Data + { + get => GetProperty(nameof(Data)); + set => SetProperty(nameof(Data), value); + } - internal override TaskActionType InternalActionType => TaskActionType.ComHandler; + internal override TaskActionType InternalActionType => TaskActionType.ComHandler; - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Action other) => base.Equals(other) && ClassId == ((ComHandlerAction)other).ClassId && Data == ((ComHandlerAction)other).Data; + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Action other) => base.Equals(other) && ClassId == ((ComHandlerAction)other).ClassId && Data == ((ComHandlerAction)other).Data; - /// Gets a string representation of the . - /// String representation of this action. - public override string ToString() => string.Format(Properties.Resources.ComHandlerAction, ClassId, Data, Id, ClassName); + /// Gets a string representation of the . + /// String representation of this action. + public override string ToString() => string.Format(Properties.Resources.ComHandlerAction, ClassId, Data, Id, ClassName); - internal static Action FromPowerShellCommand(string p) - { - var match = System.Text.RegularExpressions.Regex.Match(p, @"^\[Reflection.Assembly\]::LoadFile\('(?:[^']*)'\); \[Microsoft.Win32.TaskScheduler.TaskService\]::RunComHandlerAction\(\[GUID\]\('(?[^']*)'\), '(?[^']*)'\);?\s*$"); - return match.Success ? new ComHandlerAction(new Guid(match.Groups["g"].Value), match.Groups["d"].Value.Replace("''", "'")) : null; - } + internal static Action FromPowerShellCommand(string p) + { + var match = System.Text.RegularExpressions.Regex.Match(p, @"^\[Reflection.Assembly\]::LoadFile\('(?:[^']*)'\); \[Microsoft.Win32.TaskScheduler.TaskService\]::RunComHandlerAction\(\[GUID\]\('(?[^']*)'\), '(?[^']*)'\);?\s*$"); + return match.Success ? new ComHandlerAction(new Guid(match.Groups["g"].Value), match.Groups["d"].Value.Replace("''", "'")) : null; + } - /// Copies the properties from another the current instance. - /// The source . - internal override void CopyProperties(Action sourceAction) - { - if (sourceAction.GetType() == GetType()) + /// Copies the properties from another the current instance. + /// The source . + internal override void CopyProperties(Action sourceAction) { - base.CopyProperties(sourceAction); - ClassId = ((ComHandlerAction)sourceAction).ClassId; - Data = ((ComHandlerAction)sourceAction).Data; + if (sourceAction.GetType() == GetType()) + { + base.CopyProperties(sourceAction); + ClassId = ((ComHandlerAction)sourceAction).ClassId; + Data = ((ComHandlerAction)sourceAction).Data; + } } - } - internal override void CreateV2Action([NotNull] IActionCollection iActions) => iAction = iActions.Create(TaskActionType.ComHandler); + internal override void CreateV2Action([NotNull] IActionCollection iActions) => iAction = iActions.Create(TaskActionType.ComHandler); - internal override string GetPowerShellCommand() - { - var sb = new System.Text.StringBuilder(); - sb.Append($"[Reflection.Assembly]::LoadFile('{Assembly.GetExecutingAssembly().Location}'); "); - sb.Append($"[Microsoft.Win32.TaskScheduler.TaskService]::RunComHandlerAction([GUID]('{ClassId:D}'), '{Data?.Replace("'", "''") ?? string.Empty}'); "); - return sb.ToString(); - } + internal override string GetPowerShellCommand() + { + var sb = new System.Text.StringBuilder(); + sb.Append($"[Reflection.Assembly]::LoadFile('{Assembly.GetExecutingAssembly().Location}'); "); + sb.Append($"[Microsoft.Win32.TaskScheduler.TaskService]::RunComHandlerAction([GUID]('{ClassId:D}'), '{Data?.Replace("'", "''") ?? string.Empty}'); "); + return sb.ToString(); + } - /// Gets the name for CLSID. - /// The unique identifier. - /// - [CanBeNull] - private static string GetNameForCLSID(Guid guid) - { - using (var k = Registry.ClassesRoot.OpenSubKey("CLSID", false)) + /// Gets the name for CLSID. + /// The unique identifier. + /// + [CanBeNull] + private static string GetNameForCLSID(Guid guid) { - if (k != null) + using (var k = Registry.ClassesRoot.OpenSubKey("CLSID", false)) { - using (var k2 = k.OpenSubKey(guid.ToString("B"), false)) + if (k != null) { - return k2?.GetValue(null) as string; - } + using (var k2 = k.OpenSubKey(guid.ToString("B"), false)) + { + return k2?.GetValue(null) as string; + } + } } + return null; } - return null; } - } - - /// - /// Represents an action that sends an e-mail. Only available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and - /// later.This action has been deprecated in Windows 8 and later. However, this library is able to mimic its - /// functionality using PowerShell if the property is set to . To disable this conversion, set the value to . - /// - /// The EmailAction allows for an email to be sent when the task is triggered. - /// - /// - /// - /// - /// - [XmlType(IncludeInSchema = true)] - [XmlRoot("SendEmail", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class EmailAction : Action, IBindAsExecAction - { - private const string ImportanceHeader = "Importance"; - - private NamedValueCollection nvc; - private bool validateAttachments = true; - - /// Creates an unbound instance of . - public EmailAction() { } - /// Creates an unbound instance of . - /// Subject of the e-mail. - /// E-mail address that you want to send the e-mail from. - /// E-mail address or addresses that you want to send the e-mail to. - /// Body of the e-mail that contains the e-mail message. - /// Name of the server that you use to send e-mail from. - public EmailAction([CanBeNull] string subject, [NotNull] string from, [NotNull] string to, [CanBeNull] string body, [NotNull] string mailServer) - { - Subject = subject; - From = from; - To = to; - Body = body; - Server = mailServer; - } + /// + /// Represents an action that sends an e-mail. Only available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and + /// later.This action has been deprecated in Windows 8 and later. However, this library is able to mimic its + /// functionality using PowerShell if the property is set to . To disable this conversion, set the value to . + /// + /// The EmailAction allows for an email to be sent when the task is triggered. + /// + /// + /// + /// + /// + [XmlType(IncludeInSchema = true)] + [XmlRoot("SendEmail", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class EmailAction : Action, IBindAsExecAction + { + private const string ImportanceHeader = "Importance"; + + private NamedValueCollection nvc; + private bool validateAttachments = true; + + /// Creates an unbound instance of . + public EmailAction() { } + + /// Creates an unbound instance of . + /// Subject of the e-mail. + /// E-mail address that you want to send the e-mail from. + /// E-mail address or addresses that you want to send the e-mail to. + /// Body of the e-mail that contains the e-mail message. + /// Name of the server that you use to send e-mail from. + public EmailAction([CanBeNull] string subject, [NotNull] string from, [NotNull] string to, [CanBeNull] string body, [NotNull] string mailServer) + { + Subject = subject; + From = from; + To = to; + Body = body; + Server = mailServer; + } - internal EmailAction([NotNull] ITask task) : base(task) - { - } + internal EmailAction([NotNull] ITask task) : base(task) + { + } - internal EmailAction([NotNull] IAction action) : base(action) - { - } + internal EmailAction([NotNull] IAction action) : base(action) + { + } - /// - /// Gets or sets an array of file paths to be sent as attachments with the e-mail. Each item must be a value - /// containing a path to file. - /// - [XmlArray("Attachments", IsNullable = true)] - [XmlArrayItem("File", typeof(string))] - [DefaultValue(null)] - public object[] Attachments - { - get => GetProperty(nameof(Attachments)); - set + /// + /// Gets or sets an array of file paths to be sent as attachments with the e-mail. Each item must be a value + /// containing a path to file. + /// + [XmlArray("Attachments", IsNullable = true)] + [XmlArrayItem("File", typeof(string))] + [DefaultValue(null)] + public object[] Attachments { - if (value != null) + get => GetProperty(nameof(Attachments)); + set { - if (value.Length > 8) - throw new ArgumentOutOfRangeException(nameof(Attachments), @"Attachments array cannot contain more than 8 items."); - if (validateAttachments) + if (value != null) { - foreach (var o in value) - if (!(o is string) || !System.IO.File.Exists((string)o)) - throw new ArgumentException(@"Each value of the array must contain a valid file reference.", nameof(Attachments)); + if (value.Length > 8) + throw new ArgumentOutOfRangeException(nameof(Attachments), @"Attachments array cannot contain more than 8 items."); + if (validateAttachments) + { + foreach (var o in value) + if (!(o is string) || !System.IO.File.Exists((string)o)) + throw new ArgumentException(@"Each value of the array must contain a valid file reference.", nameof(Attachments)); + } } + if (iAction == null && (value == null || value.Length == 0)) + { + unboundValues.Remove(nameof(Attachments)); + OnPropertyChanged(nameof(Attachments)); + } + else + SetProperty(nameof(Attachments), value); } - if (iAction == null && (value == null || value.Length == 0)) - { - unboundValues.Remove(nameof(Attachments)); - OnPropertyChanged(nameof(Attachments)); - } - else - SetProperty(nameof(Attachments), value); } - } - /// Gets or sets the e-mail address or addresses that you want to Bcc in the e-mail. - [DefaultValue(null)] - public string Bcc - { - get => GetProperty(nameof(Bcc)); - set => SetProperty(nameof(Bcc), value); - } + /// Gets or sets the e-mail address or addresses that you want to Bcc in the e-mail. + [DefaultValue(null)] + public string Bcc + { + get => GetProperty(nameof(Bcc)); + set => SetProperty(nameof(Bcc), value); + } - /// Gets or sets the body of the e-mail that contains the e-mail message. - [DefaultValue(null)] - public string Body - { - get => GetProperty(nameof(Body)); - set => SetProperty(nameof(Body), value); - } + /// Gets or sets the body of the e-mail that contains the e-mail message. + [DefaultValue(null)] + public string Body + { + get => GetProperty(nameof(Body)); + set => SetProperty(nameof(Body), value); + } - /// Gets or sets the e-mail address or addresses that you want to Cc in the e-mail. - [DefaultValue(null)] - public string Cc - { - get => GetProperty(nameof(Cc)); - set => SetProperty(nameof(Cc), value); - } + /// Gets or sets the e-mail address or addresses that you want to Cc in the e-mail. + [DefaultValue(null)] + public string Cc + { + get => GetProperty(nameof(Cc)); + set => SetProperty(nameof(Cc), value); + } - /// Gets or sets the e-mail address that you want to send the e-mail from. - [DefaultValue(null)] - public string From - { - get => GetProperty(nameof(From)); - set => SetProperty(nameof(From), value); - } + /// Gets or sets the e-mail address that you want to send the e-mail from. + [DefaultValue(null)] + public string From + { + get => GetProperty(nameof(From)); + set => SetProperty(nameof(From), value); + } - /// Gets or sets the header information in the e-mail message to send. - [XmlArray] - [XmlArrayItem("HeaderField", typeof(NameValuePair))] - [NotNull] - public NamedValueCollection HeaderFields - { - get + /// Gets or sets the header information in the e-mail message to send. + [XmlArray] + [XmlArrayItem("HeaderField", typeof(NameValuePair))] + [NotNull] + public NamedValueCollection HeaderFields { - if (nvc == null) + get { - nvc = iAction == null ? new NamedValueCollection() : new NamedValueCollection(((IEmailAction)iAction).HeaderFields); - nvc.AttributedXmlFormat = false; - nvc.CollectionChanged += (o, e) => OnPropertyChanged(nameof(HeaderFields)); + if (nvc == null) + { + nvc = iAction == null ? new NamedValueCollection() : new NamedValueCollection(((IEmailAction)iAction).HeaderFields); + nvc.AttributedXmlFormat = false; + nvc.CollectionChanged += (o, e) => OnPropertyChanged(nameof(HeaderFields)); + } + return nvc; } - return nvc; } - } - /// Gets or sets the priority of the e-mail message. - /// A that contains the priority of this message. - [XmlIgnore] - [DefaultValue(typeof(System.Net.Mail.MailPriority), "Normal")] - public System.Net.Mail.MailPriority Priority - { - get + /// Gets or sets the priority of the e-mail message. + /// A that contains the priority of this message. + [XmlIgnore] + [DefaultValue(typeof(System.Net.Mail.MailPriority), "Normal")] + public System.Net.Mail.MailPriority Priority { - if (nvc != null && HeaderFields.TryGetValue(ImportanceHeader, out var s)) - return TryParse(s, System.Net.Mail.MailPriority.Normal); - return System.Net.Mail.MailPriority.Normal; + get + { + if (nvc != null && HeaderFields.TryGetValue(ImportanceHeader, out var s)) + return TryParse(s, System.Net.Mail.MailPriority.Normal); + return System.Net.Mail.MailPriority.Normal; + } + set => HeaderFields[ImportanceHeader] = value.ToString(); } - set => HeaderFields[ImportanceHeader] = value.ToString(); - } - /// Gets or sets the e-mail address that you want to reply to. - [DefaultValue(null)] - public string ReplyTo - { - get => GetProperty(nameof(ReplyTo)); - set => SetProperty(nameof(ReplyTo), value); - } + /// Gets or sets the e-mail address that you want to reply to. + [DefaultValue(null)] + public string ReplyTo + { + get => GetProperty(nameof(ReplyTo)); + set => SetProperty(nameof(ReplyTo), value); + } - /// Gets or sets the name of the server that you use to send e-mail from. - [DefaultValue(null)] - public string Server - { - get => GetProperty(nameof(Server)); - set => SetProperty(nameof(Server), value); - } + /// Gets or sets the name of the server that you use to send e-mail from. + [DefaultValue(null)] + public string Server + { + get => GetProperty(nameof(Server)); + set => SetProperty(nameof(Server), value); + } - /// Gets or sets the subject of the e-mail. - [DefaultValue(null)] - public string Subject - { - get => GetProperty(nameof(Subject)); - set => SetProperty(nameof(Subject), value); - } + /// Gets or sets the subject of the e-mail. + [DefaultValue(null)] + public string Subject + { + get => GetProperty(nameof(Subject)); + set => SetProperty(nameof(Subject), value); + } - /// Gets or sets the e-mail address or addresses that you want to send the e-mail to. - [DefaultValue(null)] - public string To - { - get => GetProperty(nameof(To)); - set => SetProperty(nameof(To), value); - } + /// Gets or sets the e-mail address or addresses that you want to send the e-mail to. + [DefaultValue(null)] + public string To + { + get => GetProperty(nameof(To)); + set => SetProperty(nameof(To), value); + } - internal override TaskActionType InternalActionType => TaskActionType.SendEmail; + internal override TaskActionType InternalActionType => TaskActionType.SendEmail; - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Action other) => base.Equals(other) && GetPowerShellCommand() == other.GetPowerShellCommand(); + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Action other) => base.Equals(other) && GetPowerShellCommand() == other.GetPowerShellCommand(); - /// Gets a string representation of the . - /// String representation of this action. - public override string ToString() => string.Format(Properties.Resources.EmailAction, Subject, To, Cc, Bcc, From, ReplyTo, Body, Server, Id); + /// Gets a string representation of the . + /// String representation of this action. + public override string ToString() => string.Format(Properties.Resources.EmailAction, Subject, To, Cc, Bcc, From, ReplyTo, Body, Server, Id); - internal static Action FromPowerShellCommand(string p) - { - var match = System.Text.RegularExpressions.Regex.Match(p, @"^Send-MailMessage -From '(?(?:[^']|'')*)' -Subject '(?(?:[^']|'')*)' -SmtpServer '(?(?:[^']|'')*)'(?: -Encoding UTF8)?(?: -To (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Cc (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Bcc (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?:(?: -BodyAsHtml)? -Body '(?(?:[^']|'')*)')?(?: -Attachments (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Priority (?High|Normal|Low))?;?\s*$"); - if (match.Success) + internal static Action FromPowerShellCommand(string p) { - var action = new EmailAction(UnPrep(FromUTF8(match.Groups["subject"].Value)), UnPrep(match.Groups["from"].Value), FromPS(match.Groups["to"]), UnPrep(FromUTF8(match.Groups["body"].Value)), UnPrep(match.Groups["server"].Value)) + var match = System.Text.RegularExpressions.Regex.Match(p, @"^Send-MailMessage -From '(?(?:[^']|'')*)' -Subject '(?(?:[^']|'')*)' -SmtpServer '(?(?:[^']|'')*)'(?: -Encoding UTF8)?(?: -To (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Cc (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Bcc (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?:(?: -BodyAsHtml)? -Body '(?(?:[^']|'')*)')?(?: -Attachments (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Priority (?High|Normal|Low))?;?\s*$"); + if (match.Success) + { + var action = new EmailAction(UnPrep(FromUTF8(match.Groups["subject"].Value)), UnPrep(match.Groups["from"].Value), FromPS(match.Groups["to"]), UnPrep(FromUTF8(match.Groups["body"].Value)), UnPrep(match.Groups["server"].Value)) { Cc = FromPS(match.Groups["cc"]), Bcc = FromPS(match.Groups["bcc"]) }; - action.validateAttachments = false; - if (match.Groups["att"].Success) - action.Attachments = Array.ConvertAll(FromPS(match.Groups["att"].Value), s => s); - action.validateAttachments = true; - if (match.Groups["imp"].Success) - action.HeaderFields[ImportanceHeader] = match.Groups["imp"].Value; - return action; + action.validateAttachments = false; + if (match.Groups["att"].Success) + action.Attachments = Array.ConvertAll(FromPS(match.Groups["att"].Value), s => s); + action.validateAttachments = true; + if (match.Groups["imp"].Success) + action.HeaderFields[ImportanceHeader] = match.Groups["imp"].Value; + return action; + } + return null; } - return null; - } - internal override void Bind(ITaskDefinition iTaskDef) - { - base.Bind(iTaskDef); - nvc?.Bind(((IEmailAction)iAction).HeaderFields); - } + internal override void Bind(ITaskDefinition iTaskDef) + { + base.Bind(iTaskDef); + nvc?.Bind(((IEmailAction)iAction).HeaderFields); + } - /// Copies the properties from another the current instance. - /// The source . - internal override void CopyProperties(Action sourceAction) - { - if (sourceAction.GetType() == GetType()) + /// Copies the properties from another the current instance. + /// The source . + internal override void CopyProperties(Action sourceAction) { - base.CopyProperties(sourceAction); - if (((EmailAction)sourceAction).Attachments != null) - Attachments = (object[])((EmailAction)sourceAction).Attachments.Clone(); - Bcc = ((EmailAction)sourceAction).Bcc; - Body = ((EmailAction)sourceAction).Body; - Cc = ((EmailAction)sourceAction).Cc; - From = ((EmailAction)sourceAction).From; - if (((EmailAction)sourceAction).nvc != null) - ((EmailAction)sourceAction).HeaderFields.CopyTo(HeaderFields); - ReplyTo = ((EmailAction)sourceAction).ReplyTo; - Server = ((EmailAction)sourceAction).Server; - Subject = ((EmailAction)sourceAction).Subject; - To = ((EmailAction)sourceAction).To; + if (sourceAction.GetType() == GetType()) + { + base.CopyProperties(sourceAction); + if (((EmailAction)sourceAction).Attachments != null) + Attachments = (object[])((EmailAction)sourceAction).Attachments.Clone(); + Bcc = ((EmailAction)sourceAction).Bcc; + Body = ((EmailAction)sourceAction).Body; + Cc = ((EmailAction)sourceAction).Cc; + From = ((EmailAction)sourceAction).From; + if (((EmailAction)sourceAction).nvc != null) + ((EmailAction)sourceAction).HeaderFields.CopyTo(HeaderFields); + ReplyTo = ((EmailAction)sourceAction).ReplyTo; + Server = ((EmailAction)sourceAction).Server; + Subject = ((EmailAction)sourceAction).Subject; + To = ((EmailAction)sourceAction).To; + } } - } - internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.SendEmail); + internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.SendEmail); - internal override string GetPowerShellCommand() - { - // Send-MailMessage [-To] [-Subject] [[-Body] ] [[-SmtpServer] ] -From - // [-Attachments ] [-Bcc ] [-BodyAsHtml] [-Cc ] [-Credential ] - // [-DeliveryNotificationOption ] [-Encoding ] [-Port ] [-Priority - // ] [-UseSsl] [ ] - var bodyIsHtml = Body != null && Body.Trim().StartsWith("<") && Body.Trim().EndsWith(">"); - var sb = new System.Text.StringBuilder(); - sb.AppendFormat("Send-MailMessage -From '{0}' -Subject '{1}' -SmtpServer '{2}' -Encoding UTF8", Prep(From), ToUTF8(Prep(Subject)), Prep(Server)); - if (!string.IsNullOrEmpty(To)) - sb.AppendFormat(" -To {0}", ToPS(To)); - if (!string.IsNullOrEmpty(Cc)) - sb.AppendFormat(" -Cc {0}", ToPS(Cc)); - if (!string.IsNullOrEmpty(Bcc)) - sb.AppendFormat(" -Bcc {0}", ToPS(Bcc)); - if (bodyIsHtml) - sb.Append(" -BodyAsHtml"); - if (!string.IsNullOrEmpty(Body)) - sb.AppendFormat(" -Body '{0}'", ToUTF8(Prep(Body))); - if (Attachments != null && Attachments.Length > 0) - sb.AppendFormat(" -Attachments {0}", ToPS(Array.ConvertAll(Attachments, o => Prep(o.ToString())))); - var hdr = new List(HeaderFields.Names); - if (hdr.Contains(ImportanceHeader)) + internal override string GetPowerShellCommand() { - var p = Priority; - if (p != System.Net.Mail.MailPriority.Normal) - sb.Append($" -Priority {p}"); - hdr.Remove(ImportanceHeader); + // Send-MailMessage [-To] [-Subject] [[-Body] ] [[-SmtpServer] ] -From + // [-Attachments ] [-Bcc ] [-BodyAsHtml] [-Cc ] [-Credential ] + // [-DeliveryNotificationOption ] [-Encoding ] [-Port ] [-Priority + // ] [-UseSsl] [ ] + var bodyIsHtml = Body != null && Body.Trim().StartsWith("<") && Body.Trim().EndsWith(">"); + var sb = new System.Text.StringBuilder(); + sb.AppendFormat("Send-MailMessage -From '{0}' -Subject '{1}' -SmtpServer '{2}' -Encoding UTF8", Prep(From), ToUTF8(Prep(Subject)), Prep(Server)); + if (!string.IsNullOrEmpty(To)) + sb.AppendFormat(" -To {0}", ToPS(To)); + if (!string.IsNullOrEmpty(Cc)) + sb.AppendFormat(" -Cc {0}", ToPS(Cc)); + if (!string.IsNullOrEmpty(Bcc)) + sb.AppendFormat(" -Bcc {0}", ToPS(Bcc)); + if (bodyIsHtml) + sb.Append(" -BodyAsHtml"); + if (!string.IsNullOrEmpty(Body)) + sb.AppendFormat(" -Body '{0}'", ToUTF8(Prep(Body))); + if (Attachments != null && Attachments.Length > 0) + sb.AppendFormat(" -Attachments {0}", ToPS(Array.ConvertAll(Attachments, o => Prep(o.ToString())))); + var hdr = new List(HeaderFields.Names); + if (hdr.Contains(ImportanceHeader)) + { + var p = Priority; + if (p != System.Net.Mail.MailPriority.Normal) + sb.Append($" -Priority {p}"); + hdr.Remove(ImportanceHeader); + } + if (hdr.Count > 0) + throw new InvalidOperationException("Under Windows 8 and later, EmailAction objects are converted to PowerShell. This action contains headers that are not supported."); + sb.Append("; "); + return sb.ToString(); + + /*var msg = new System.Net.Mail.MailMessage(this.From, this.To, this.Subject, this.Body); + if (!string.IsNullOrEmpty(this.Bcc)) + msg.Bcc.Add(this.Bcc); + if (!string.IsNullOrEmpty(this.Cc)) + msg.CC.Add(this.Cc); + if (!string.IsNullOrEmpty(this.ReplyTo)) + msg.ReplyTo = new System.Net.Mail.MailAddress(this.ReplyTo); + if (this.Attachments != null && this.Attachments.Length > 0) + foreach (string s in this.Attachments) + msg.Attachments.Add(new System.Net.Mail.Attachment(s)); + if (this.nvc != null) + foreach (var ha in this.HeaderFields) + msg.Headers.Add(ha.Name, ha.Value); + var client = new System.Net.Mail.SmtpClient(this.Server); + client.Send(msg);*/ } - if (hdr.Count > 0) - throw new InvalidOperationException("Under Windows 8 and later, EmailAction objects are converted to PowerShell. This action contains headers that are not supported."); - sb.Append("; "); - return sb.ToString(); - - /*var msg = new System.Net.Mail.MailMessage(this.From, this.To, this.Subject, this.Body); - if (!string.IsNullOrEmpty(this.Bcc)) - msg.Bcc.Add(this.Bcc); - if (!string.IsNullOrEmpty(this.Cc)) - msg.CC.Add(this.Cc); - if (!string.IsNullOrEmpty(this.ReplyTo)) - msg.ReplyTo = new System.Net.Mail.MailAddress(this.ReplyTo); - if (this.Attachments != null && this.Attachments.Length > 0) - foreach (string s in this.Attachments) - msg.Attachments.Add(new System.Net.Mail.Attachment(s)); - if (this.nvc != null) - foreach (var ha in this.HeaderFields) - msg.Headers.Add(ha.Name, ha.Value); - var client = new System.Net.Mail.SmtpClient(this.Server); - client.Send(msg);*/ - } - private static string[] FromPS(string p) - { - var list = p.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); - return Array.ConvertAll(list, i => UnPrep(i).Trim('\'')); - } - - private static string FromPS(System.Text.RegularExpressions.Group g, string delimeter = ";") => g.Success ? string.Join(delimeter, FromPS(g.Value)) : null; + private static string[] FromPS(string p) + { + var list = p.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); + return Array.ConvertAll(list, i => UnPrep(i).Trim('\'')); + } - private static string FromUTF8(string s) - { - var bytes = System.Text.Encoding.UTF8.GetBytes(s); - return System.Text.Encoding.Default.GetString(bytes); - } + private static string FromPS(System.Text.RegularExpressions.Group g, string delimeter = ";") => g.Success ? string.Join(delimeter, FromPS(g.Value)) : null; - private static string Prep(string s) => s?.Replace("'", "''"); + private static string FromUTF8(string s) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(s); + return System.Text.Encoding.Default.GetString(bytes); + } - private static string ToPS(string input, char[] delimeters = null) - { - if (delimeters == null) - delimeters = new[] { ';', ',' }; - return ToPS(Array.ConvertAll(input.Split(delimeters), i => Prep(i.Trim()))); - } + private static string Prep(string s) => s?.Replace("'", "''"); - private static string ToPS(string[] input) => string.Join(", ", Array.ConvertAll(input, i => string.Concat("'", i.Trim(), "'"))); + private static string ToPS(string input, char[] delimeters = null) + { + if (delimeters == null) + delimeters = new[] { ';', ',' }; + return ToPS(Array.ConvertAll(input.Split(delimeters), i => Prep(i.Trim()))); + } - private static string ToUTF8(string s) - { - if (s == null) return null; - var bytes = System.Text.Encoding.Default.GetBytes(s); - return System.Text.Encoding.UTF8.GetString(bytes); - } + private static string ToPS(string[] input) => string.Join(", ", Array.ConvertAll(input, i => string.Concat("'", i.Trim(), "'"))); - private static string UnPrep(string s) => s?.Replace("''", "'"); - } + private static string ToUTF8(string s) + { + if (s == null) return null; + var bytes = System.Text.Encoding.Default.GetBytes(s); + return System.Text.Encoding.UTF8.GetString(bytes); + } - /// Represents an action that executes a command-line operation. - /// - /// All versions of the base library support the ExecAction. It only has three properties that allow it to run an executable with parameters. - /// - /// - /// - /// - /// - /// - [XmlRoot("Exec", Namespace = TaskDefinition.tns, IsNullable = false)] - public class ExecAction : Action - { + private static string UnPrep(string s) => s?.Replace("''", "'"); + } + + /// Represents an action that executes a command-line operation. + /// + /// All versions of the base library support the ExecAction. It only has three properties that allow it to run an executable with parameters. + /// + /// + /// + /// + /// + /// + [XmlRoot("Exec", Namespace = TaskDefinition.tns, IsNullable = false)] + public class ExecAction : Action + { #if DEBUG internal const string PowerShellArgFormat = "-NoExit -Command \"& {{<# {0}:{1} #> {2}}}\""; #else - internal const string PowerShellArgFormat = "-NoLogo -NonInteractive -WindowStyle Hidden -Command \"& {{<# {0}:{1} #> {2}}}\""; + internal const string PowerShellArgFormat = "-NoLogo -NonInteractive -WindowStyle Hidden -Command \"& {{<# {0}:{1} #> {2}}}\""; #endif - internal const string PowerShellPath = "powershell"; - internal const string ScriptIdentifer = "TSML_20140424"; - - /// Creates a new instance of an that can be added to . - public ExecAction() { } - - /// Creates a new instance of an that can be added to . - /// Path to an executable file. - /// Arguments associated with the command-line operation. This value can be null. - /// - /// Directory that contains either the executable file or the files that are used by the executable file. This value can be null. - /// - public ExecAction([NotNull] string path, string arguments = null, string workingDirectory = null) - { - Path = path; - Arguments = arguments; - WorkingDirectory = workingDirectory; - } - - internal ExecAction([NotNull] ITask task) : base(task) - { - } - - internal ExecAction([NotNull] IAction action) : base(action) - { - } - - /// Gets or sets the arguments associated with the command-line operation. - [DefaultValue("")] - public string Arguments - { - get + internal const string PowerShellPath = "powershell"; + internal const string ScriptIdentifer = "TSML_20140424"; + + /// Creates a new instance of an that can be added to . + public ExecAction() { } + + /// Creates a new instance of an that can be added to . + /// Path to an executable file. + /// Arguments associated with the command-line operation. This value can be null. + /// + /// Directory that contains either the executable file or the files that are used by the executable file. This value can be null. + /// + public ExecAction([NotNull] string path, string arguments = null, string workingDirectory = null) { - if (v1Task != null) - return v1Task.GetParameters(); - return GetProperty(nameof(Arguments), ""); + Path = path; + Arguments = arguments; + WorkingDirectory = workingDirectory; } - set + + internal ExecAction([NotNull] ITask task) : base(task) { - if (v1Task != null) - v1Task.SetParameters(value); - else - SetProperty(nameof(Arguments), value); } - } - /// Gets or sets the path to an executable file. - [XmlElement("Command")] - [DefaultValue("")] - public string Path - { - get + internal ExecAction([NotNull] IAction action) : base(action) { - if (v1Task != null) - return v1Task.GetApplicationName(); - return GetProperty(nameof(Path), ""); } - set + + /// Gets or sets the arguments associated with the command-line operation. + [DefaultValue("")] + public string Arguments { - if (v1Task != null) - v1Task.SetApplicationName(value); - else - SetProperty(nameof(Path), value); + get + { + if (v1Task != null) + return v1Task.GetParameters(); + return GetProperty(nameof(Arguments), ""); + } + set + { + if (v1Task != null) + v1Task.SetParameters(value); + else + SetProperty(nameof(Arguments), value); + } } - } - /// - /// Gets or sets the directory that contains either the executable file or the files that are used by the executable file. - /// - [DefaultValue("")] - public string WorkingDirectory - { - get + /// Gets or sets the path to an executable file. + [XmlElement("Command")] + [DefaultValue("")] + public string Path { - if (v1Task != null) - return v1Task.GetWorkingDirectory(); - return GetProperty(nameof(WorkingDirectory), ""); + get + { + if (v1Task != null) + return v1Task.GetApplicationName(); + return GetProperty(nameof(Path), ""); + } + set + { + if (v1Task != null) + v1Task.SetApplicationName(value); + else + SetProperty(nameof(Path), value); + } } - set + + /// + /// Gets or sets the directory that contains either the executable file or the files that are used by the executable file. + /// + [DefaultValue("")] + public string WorkingDirectory { - if (v1Task != null) - v1Task.SetWorkingDirectory(value); - else - SetProperty(nameof(WorkingDirectory), value); + get + { + if (v1Task != null) + return v1Task.GetWorkingDirectory(); + return GetProperty(nameof(WorkingDirectory), ""); + } + set + { + if (v1Task != null) + v1Task.SetWorkingDirectory(value); + else + SetProperty(nameof(WorkingDirectory), value); + } } - } - internal override TaskActionType InternalActionType => TaskActionType.Execute; + internal override TaskActionType InternalActionType => TaskActionType.Execute; - /// Determines whether the specified path is a valid filename and, optionally, if it exists. - /// The path. - /// if set to true check if file exists. - /// if set to true throw exception on error. - /// true if the specified path is a valid filename; otherwise, false. - public static bool IsValidPath(string path, bool checkIfExists = true, bool throwOnException = false) - { - try + /// Determines whether the specified path is a valid filename and, optionally, if it exists. + /// The path. + /// if set to true check if file exists. + /// if set to true throw exception on error. + /// true if the specified path is a valid filename; otherwise, false. + public static bool IsValidPath(string path, bool checkIfExists = true, bool throwOnException = false) { - if (path == null) throw new ArgumentNullException(nameof(path)); - /*if (path.StartsWith("\"") && path.EndsWith("\"") && path.Length > 1) - path = path.Substring(1, path.Length - 2);*/ - var fn = System.IO.Path.GetFileName(path); - System.Diagnostics.Debug.WriteLine($"IsValidPath fn={fn}"); - if (fn == string.Empty) - return false; - var dn = System.IO.Path.GetDirectoryName(path); - System.Diagnostics.Debug.WriteLine($"IsValidPath dir={dn ?? "null"}"); - System.IO.Path.GetFullPath(path); - return true; + try + { + if (path == null) throw new ArgumentNullException(nameof(path)); + /*if (path.StartsWith("\"") && path.EndsWith("\"") && path.Length > 1) + path = path.Substring(1, path.Length - 2);*/ + var fn = System.IO.Path.GetFileName(path); + System.Diagnostics.Debug.WriteLine($"IsValidPath fn={fn}"); + if (fn == string.Empty) + return false; + var dn = System.IO.Path.GetDirectoryName(path); + System.Diagnostics.Debug.WriteLine($"IsValidPath dir={dn ?? "null"}"); + System.IO.Path.GetFullPath(path); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"IsValidPath exc={ex}"); + if (throwOnException) throw; + } + return false; } - catch (Exception ex) + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Action other) => base.Equals(other) && Path == ((ExecAction)other).Path && Arguments == ((ExecAction)other).Arguments && WorkingDirectory == ((ExecAction)other).WorkingDirectory; + + /// + /// Validates the input as a valid filename and optionally checks for its existence. If valid, the property is + /// set to the validated absolute file path. + /// + /// The file path to validate. + /// if set to true check if the file exists. + public void SetValidatedPath([NotNull] string path, bool checkIfExists = true) { - System.Diagnostics.Debug.WriteLine($"IsValidPath exc={ex}"); - if (throwOnException) throw; + if (IsValidPath(path, checkIfExists, true)) + Path = path; } - return false; - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Action other) => base.Equals(other) && Path == ((ExecAction)other).Path && Arguments == ((ExecAction)other).Arguments && WorkingDirectory == ((ExecAction)other).WorkingDirectory; - /// - /// Validates the input as a valid filename and optionally checks for its existence. If valid, the property is - /// set to the validated absolute file path. - /// - /// The file path to validate. - /// if set to true check if the file exists. - public void SetValidatedPath([NotNull] string path, bool checkIfExists = true) - { - if (IsValidPath(path, checkIfExists, true)) - Path = path; - } + /// Gets a string representation of the . + /// String representation of this action. + public override string ToString() => string.Format(Properties.Resources.ExecAction, Path, Arguments, WorkingDirectory, Id); - /// Gets a string representation of the . - /// String representation of this action. - public override string ToString() => string.Format(Properties.Resources.ExecAction, Path, Arguments, WorkingDirectory, Id); + internal static string BuildPowerShellCmd(string actionType, string cmd) => string.Format(PowerShellArgFormat, ScriptIdentifer, actionType, cmd); - internal static string BuildPowerShellCmd(string actionType, string cmd) => string.Format(PowerShellArgFormat, ScriptIdentifer, actionType, cmd); + internal static ExecAction ConvertToPowerShellAction(Action action) => CreatePowerShellAction(action.ActionType.ToString(), action.GetPowerShellCommand()); - internal static ExecAction ConvertToPowerShellAction(Action action) => CreatePowerShellAction(action.ActionType.ToString(), action.GetPowerShellCommand()); + internal static ExecAction CreatePowerShellAction(string actionType, string cmd) => new ExecAction(PowerShellPath, BuildPowerShellCmd(actionType, cmd)); - internal static ExecAction CreatePowerShellAction(string actionType, string cmd) => new ExecAction(PowerShellPath, BuildPowerShellCmd(actionType, cmd)); - - internal static Action FromPowerShellCommand(string p) - { - var match = System.Text.RegularExpressions.Regex.Match(p, "^Start-Process -FilePath '(?

[^']*)'(?: -ArgumentList '(?[^']*)')?(?: -WorkingDirectory '(?[^']*)')?;?\\s*$"); - return match.Success ? new ExecAction(match.Groups["p"].Value, match.Groups["a"].Success ? match.Groups["a"].Value.Replace("''", "'") : null, match.Groups["d"].Success ? match.Groups["d"].Value : null) : null; - } + internal static Action FromPowerShellCommand(string p) + { + var match = System.Text.RegularExpressions.Regex.Match(p, "^Start-Process -FilePath '(?

[^']*)'(?: -ArgumentList '(?[^']*)')?(?: -WorkingDirectory '(?[^']*)')?;?\\s*$"); + return match.Success ? new ExecAction(match.Groups["p"].Value, match.Groups["a"].Success ? match.Groups["a"].Value.Replace("''", "'") : null, match.Groups["d"].Success ? match.Groups["d"].Value : null) : null; + } - ///

Copies the properties from another the current instance. - /// The source . - internal override void CopyProperties(Action sourceAction) - { - if (sourceAction.GetType() == GetType()) + /// Copies the properties from another the current instance. + /// The source . + internal override void CopyProperties(Action sourceAction) { - base.CopyProperties(sourceAction); - Path = ((ExecAction)sourceAction).Path; - Arguments = ((ExecAction)sourceAction).Arguments; - WorkingDirectory = ((ExecAction)sourceAction).WorkingDirectory; + if (sourceAction.GetType() == GetType()) + { + base.CopyProperties(sourceAction); + Path = ((ExecAction)sourceAction).Path; + Arguments = ((ExecAction)sourceAction).Arguments; + WorkingDirectory = ((ExecAction)sourceAction).WorkingDirectory; + } } - } - internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.Execute); + internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.Execute); - internal override string GetPowerShellCommand() - { - var sb = new System.Text.StringBuilder($"Start-Process -FilePath '{Path}'"); - if (!string.IsNullOrEmpty(Arguments)) - sb.Append($" -ArgumentList '{Arguments.Replace("'", "''")}'"); - if (!string.IsNullOrEmpty(WorkingDirectory)) - sb.Append($" -WorkingDirectory '{WorkingDirectory}'"); - return sb.Append("; ").ToString(); - } + internal override string GetPowerShellCommand() + { + var sb = new System.Text.StringBuilder($"Start-Process -FilePath '{Path}'"); + if (!string.IsNullOrEmpty(Arguments)) + sb.Append($" -ArgumentList '{Arguments.Replace("'", "''")}'"); + if (!string.IsNullOrEmpty(WorkingDirectory)) + sb.Append($" -WorkingDirectory '{WorkingDirectory}'"); + return sb.Append("; ").ToString(); + } - internal string[] ParsePowerShellItems() - { - if (((Path?.EndsWith(PowerShellPath, StringComparison.InvariantCultureIgnoreCase) ?? false) || - (Path?.EndsWith(PowerShellPath + ".exe", StringComparison.InvariantCultureIgnoreCase) ?? false)) && (Arguments?.Contains(ScriptIdentifer) ?? false)) + internal string[] ParsePowerShellItems() { - var match = System.Text.RegularExpressions.Regex.Match(Arguments, @"<# " + ScriptIdentifer + ":(?\\w+) #> (?.+)}\"$"); - if (match.Success) - return new[] { match.Groups["type"].Value, match.Groups["cmd"].Value }; + if (((Path?.EndsWith(PowerShellPath, StringComparison.InvariantCultureIgnoreCase) ?? false) || + (Path?.EndsWith(PowerShellPath + ".exe", StringComparison.InvariantCultureIgnoreCase) ?? false)) && (Arguments?.Contains(ScriptIdentifer) ?? false)) + { + var match = System.Text.RegularExpressions.Regex.Match(Arguments, @"<# " + ScriptIdentifer + ":(?\\w+) #> (?.+)}\"$"); + if (match.Success) + return new[] { match.Groups["type"].Value, match.Groups["cmd"].Value }; + } + return null; } - return null; } - } - /// - /// Represents an action that shows a message box when a task is activated. Only available for Task Scheduler 2.0 on Windows Vista - /// or Windows Server 2003 and later.This action has been deprecated in Windows 8 and later. However, this - /// library is able to mimic its functionality using PowerShell if the property is - /// set to . To disable this conversion, set the value to . - /// - /// Display a message when the trigger fires using the ShowMessageAction. - /// - /// - /// - /// - /// - [XmlType(IncludeInSchema = true)] - [XmlRoot("ShowMessage", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class ShowMessageAction : Action, IBindAsExecAction - { - /// Creates a new unbound instance of . - public ShowMessageAction() - { - } + /// + /// Represents an action that shows a message box when a task is activated. Only available for Task Scheduler 2.0 on Windows Vista + /// or Windows Server 2003 and later.This action has been deprecated in Windows 8 and later. However, this + /// library is able to mimic its functionality using PowerShell if the property is + /// set to . To disable this conversion, set the value to . + /// + /// Display a message when the trigger fires using the ShowMessageAction. + /// + /// + /// + /// + /// + [XmlType(IncludeInSchema = true)] + [XmlRoot("ShowMessage", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class ShowMessageAction : Action, IBindAsExecAction + { + /// Creates a new unbound instance of . + public ShowMessageAction() + { + } - /// Creates a new unbound instance of . - /// Message text that is displayed in the body of the message box. - /// Title of the message box. - public ShowMessageAction([CanBeNull] string messageBody, [CanBeNull] string title) - { - MessageBody = messageBody; - Title = title; - } + /// Creates a new unbound instance of . + /// Message text that is displayed in the body of the message box. + /// Title of the message box. + public ShowMessageAction([CanBeNull] string messageBody, [CanBeNull] string title) + { + MessageBody = messageBody; + Title = title; + } - internal ShowMessageAction([NotNull] ITask task) : base(task) - { - } + internal ShowMessageAction([NotNull] ITask task) : base(task) + { + } - internal ShowMessageAction([NotNull] IAction action) : base(action) - { - } + internal ShowMessageAction([NotNull] IAction action) : base(action) + { + } - /// Gets or sets the message text that is displayed in the body of the message box. - [XmlElement("Body")] - [DefaultValue(null)] - public string MessageBody - { - get => GetProperty(nameof(MessageBody)); - set => SetProperty(nameof(MessageBody), value); - } + /// Gets or sets the message text that is displayed in the body of the message box. + [XmlElement("Body")] + [DefaultValue(null)] + public string MessageBody + { + get => GetProperty(nameof(MessageBody)); + set => SetProperty(nameof(MessageBody), value); + } - /// Gets or sets the title of the message box. - [DefaultValue(null)] - public string Title - { - get => GetProperty(nameof(Title)); - set => SetProperty(nameof(Title), value); - } + /// Gets or sets the title of the message box. + [DefaultValue(null)] + public string Title + { + get => GetProperty(nameof(Title)); + set => SetProperty(nameof(Title), value); + } - internal override TaskActionType InternalActionType => TaskActionType.ShowMessage; + internal override TaskActionType InternalActionType => TaskActionType.ShowMessage; - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Action other) => base.Equals(other) && - string.Equals(Title, (other as ShowMessageAction)?.Title) && - string.Equals(MessageBody, - (other as ShowMessageAction)?.MessageBody); + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Action other) => base.Equals(other) && + string.Equals(Title, (other as ShowMessageAction)?.Title) && + string.Equals(MessageBody, + (other as ShowMessageAction)?.MessageBody); - /// Gets a string representation of the . - /// String representation of this action. - public override string ToString() => - string.Format(Properties.Resources.ShowMessageAction, Title, MessageBody, Id); + /// Gets a string representation of the . + /// String representation of this action. + public override string ToString() => + string.Format(Properties.Resources.ShowMessageAction, Title, MessageBody, Id); - internal static Action FromPowerShellCommand(string p) - { - var match = System.Text.RegularExpressions.Regex.Match(p, - @"^\[System.Reflection.Assembly\]::LoadWithPartialName\('System.Windows.Forms'\); \[System.Windows.Forms.MessageBox\]::Show\('(?(?:[^']|'')*)'(?:,'(?(?:[^']|'')*)')?\);?\s*$"); - return match.Success - ? new ShowMessageAction(match.Groups["msg"].Value.Replace("''", "'"), - match.Groups["t"].Success ? match.Groups["t"].Value.Replace("''", "'") : null) - : null; - } + internal static Action FromPowerShellCommand(string p) + { + var match = System.Text.RegularExpressions.Regex.Match(p, + @"^\[System.Reflection.Assembly\]::LoadWithPartialName\('System.Windows.Forms'\); \[System.Windows.Forms.MessageBox\]::Show\('(?(?:[^']|'')*)'(?:,'(?(?:[^']|'')*)')?\);?\s*$"); + return match.Success + ? new ShowMessageAction(match.Groups["msg"].Value.Replace("''", "'"), + match.Groups["t"].Success ? match.Groups["t"].Value.Replace("''", "'") : null) + : null; + } - /// Copies the properties from another the current instance. - /// The source . - internal override void CopyProperties(Action sourceAction) - { - if (sourceAction.GetType() == GetType()) + /// Copies the properties from another the current instance. + /// The source . + internal override void CopyProperties(Action sourceAction) { - base.CopyProperties(sourceAction); - Title = ((ShowMessageAction) sourceAction).Title; - MessageBody = ((ShowMessageAction) sourceAction).MessageBody; + if (sourceAction.GetType() == GetType()) + { + base.CopyProperties(sourceAction); + Title = ((ShowMessageAction)sourceAction).Title; + MessageBody = ((ShowMessageAction)sourceAction).MessageBody; + } } - } - internal override void CreateV2Action(IActionCollection iActions) => - iAction = iActions.Create(TaskActionType.ShowMessage); + internal override void CreateV2Action(IActionCollection iActions) => + iAction = iActions.Create(TaskActionType.ShowMessage); - internal override string GetPowerShellCommand() - { - // [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Your_Desired_Message','Your_Desired_Title'); - var sb = new System.Text.StringBuilder( - "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('"); - sb.Append(MessageBody.Replace("'", "''")); - if (Title != null) + internal override string GetPowerShellCommand() { - sb.Append("','"); - sb.Append(Title.Replace("'", "''")); - } + // [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Your_Desired_Message','Your_Desired_Title'); + var sb = new System.Text.StringBuilder( + "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('"); + sb.Append(MessageBody.Replace("'", "''")); + if (Title != null) + { + sb.Append("','"); + sb.Append(Title.Replace("'", "''")); + } - sb.Append("'); "); - return sb.ToString(); + sb.Append("'); "); + return sb.ToString(); + } } - } } } \ No newline at end of file diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/ActionCollection.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/ActionCollection.cs index 8e8032fc1..5ec7716d5 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/ActionCollection.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/ActionCollection.cs @@ -3,11 +3,8 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using System.Xml.Serialization; using winPEAS.TaskScheduler.TaskEditor.Native; using winPEAS.TaskScheduler.V1; @@ -15,762 +12,762 @@ namespace winPEAS.TaskScheduler { - /// Options for when to convert actions to PowerShell equivalents. - [Flags] - public enum PowerShellActionPlatformOption - { - /// - /// Never convert any actions to PowerShell. This will force exceptions to be thrown when unsupported actions our action quantities - /// are found. - /// - Never = 0, - - /// - /// Convert actions under Version 1 of the library (Windows XP or Windows Server 2003 and earlier). This option supports multiple - /// actions of all types. If not specified, only a single is supported. Developer must ensure that - /// PowerShell v2 or higher is installed on the target computer. - /// - Version1 = 1, - - /// - /// Convert all and references to their PowerShell equivalents on systems - /// on or after Windows 8 / Server 2012. - /// - Version2 = 2, - - /// Convert all actions regardless of version or operating system. - All = 3 - } - - /// Collection that contains the actions that are performed by the task. - [XmlRoot("Actions", Namespace = TaskDefinition.tns, IsNullable = false)] - [PublicAPI] - public sealed class ActionCollection : IList, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged - { - internal const int MaxActions = 32; - private const string IndexerName = "Item[]"; - private static readonly string psV2IdRegex = $"(?:; )?{nameof(PowerShellConversion)}=(?0|1)"; - private bool inV2set; - private PowerShellActionPlatformOption psConvert = PowerShellActionPlatformOption.Version2; - private readonly List v1Actions; - private ITask v1Task; - private readonly IActionCollection v2Coll; - private ITaskDefinition v2Def; - - internal ActionCollection([NotNull] ITask task) - { - v1Task = task; - v1Actions = GetV1Actions(); - PowerShellConversion = Action.TryParse(v1Task.GetDataItem(nameof(PowerShellConversion)), psConvert | PowerShellActionPlatformOption.Version2); - } - - internal ActionCollection([NotNull] ITaskDefinition iTaskDef) - { - v2Def = iTaskDef; - v2Coll = iTaskDef.Actions; - System.Text.RegularExpressions.Match match; - if (iTaskDef.Data != null && (match = System.Text.RegularExpressions.Regex.Match(iTaskDef.Data, psV2IdRegex)).Success) - { - var on = false; - try { on = bool.Parse(match.Groups["v"].Value); } catch { try { on = int.Parse(match.Groups["v"].Value) == 1; } catch { } } - if (on) - psConvert |= PowerShellActionPlatformOption.Version2; - else - psConvert &= ~PowerShellActionPlatformOption.Version2; - } - UnconvertUnsupportedActions(); - } - - /// Occurs when a collection changes. - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets or sets the identifier of the principal for the task. - [XmlAttribute] - [DefaultValue(null)] - [CanBeNull] - public string Context - { - get - { - if (v2Coll != null) - return v2Coll.Context; - return v1Task.GetDataItem("ActionCollectionContext"); - } - set - { - if (v2Coll != null) - v2Coll.Context = value; - else - v1Task.SetDataItem("ActionCollectionContext", value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the systems under which unsupported actions will be converted to PowerShell instances. - /// - /// The PowerShell platform options. - /// - /// This property will affect how many actions are physically stored in the system and is tied to the version of Task Scheduler. - /// - /// If set to , then no actions will ever be converted to PowerShell. This will - /// force exceptions to be thrown when unsupported actions our action quantities are found. - /// - /// - /// If set to , then actions will be converted only under Version 1 of the - /// library (Windows XP or Windows Server 2003 and earlier). This option supports multiple actions of all types. If not specified, - /// only a single is supported. Developer must ensure that PowerShell v2 or higher is installed on the - /// target computer. - /// - /// - /// If set to (which is the default value), then and references will be converted to their PowerShell equivalents on systems - /// on or after Windows 8 / Server 2012. - /// - /// - /// If set to , then any actions not supported by the Task Scheduler version will be - /// converted to PowerShell. - /// - /// - [DefaultValue(typeof(PowerShellActionPlatformOption), "Version2")] - public PowerShellActionPlatformOption PowerShellConversion - { - get => psConvert; - set - { - if (psConvert != value) - { - psConvert = value; - if (v1Task != null) - v1Task.SetDataItem(nameof(PowerShellConversion), value.ToString()); - if (v2Def != null) - { - if (!string.IsNullOrEmpty(v2Def.Data)) - v2Def.Data = System.Text.RegularExpressions.Regex.Replace(v2Def.Data, psV2IdRegex, ""); - if (!SupportV2Conversion) - v2Def.Data = string.Format("{0}; {1}=0", v2Def.Data, nameof(PowerShellConversion)); - } - OnNotifyPropertyChanged(); - } - } - } - - /// Gets or sets an XML-formatted version of the collection. - public string XmlText - { - get - { - if (v2Coll != null) - return v2Coll.XmlText; - return XmlSerializationHelper.WriteObjectToXmlText(this); - } - set - { - if (v2Coll != null) - v2Coll.XmlText = value; - else - XmlSerializationHelper.ReadObjectFromXmlText(value, this); - OnNotifyPropertyChanged(); - } - } - - /// Gets the number of actions in the collection. - public int Count => (v2Coll != null) ? v2Coll.Count : v1Actions.Count; - - bool IList.IsFixedSize => false; - bool ICollection.IsReadOnly => false; - bool IList.IsReadOnly => false; - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot => this; - private bool SupportV1Conversion => (PowerShellConversion & PowerShellActionPlatformOption.Version1) != 0; - - private bool SupportV2Conversion => (PowerShellConversion & PowerShellActionPlatformOption.Version2) != 0; - - /// Gets or sets a specified action from the collection. - /// The . - /// The id ( ) of the action to be retrieved. - /// Specialized instance. - /// - /// - /// - /// Mismatching Id for action and lookup. - [NotNull] - public Action this[string actionId] - { - get - { - if (string.IsNullOrEmpty(actionId)) - throw new ArgumentNullException(nameof(actionId)); - var t = Find(a => string.Equals(a.Id, actionId)); - if (t != null) - return t; - throw new ArgumentOutOfRangeException(nameof(actionId)); - } - set - { - if (value == null) - throw new NullReferenceException(); - if (string.IsNullOrEmpty(actionId)) - throw new ArgumentNullException(nameof(actionId)); - var index = IndexOf(actionId); - value.Id = actionId; - if (index >= 0) - this[index] = value; - else - Add(value); - } - } - - /// Gets or sets a an action at the specified index. - /// The zero-based index of the action to get or set. - [NotNull] - public Action this[int index] - { - get - { - if (v2Coll != null) - return Action.CreateAction(v2Coll[++index]); - if (v1Task != null) - { - if (SupportV1Conversion) - return v1Actions[index]; - else - { - if (index == 0) - return v1Actions[0]; - } - } - throw new ArgumentOutOfRangeException(); - } - set - { - if (index < 0 || Count <= index) - throw new ArgumentOutOfRangeException(nameof(index), index, "Index is not a valid index in the ActionCollection"); - var orig = this[index].Clone(); - if (v2Coll != null) - { - inV2set = true; - try - { - Insert(index, value); - RemoveAt(index + 1); - } - finally - { - inV2set = false; - } - } - else - { - v1Actions[index] = value; - SaveV1Actions(); - } - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index)); - } - } - - object IList.this[int index] - { - get => this[index]; - set => this[index] = (Action)value; - } - - /// Adds an action to the task. - /// A type derived from . - /// A derived class. - /// The bound that was added to the collection. - [NotNull] - public TAction Add([NotNull] TAction action) where TAction : Action - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - if (v2Def != null) - action.Bind(v2Def); - else - { - if (!SupportV1Conversion && (v1Actions.Count >= 1 || !(action is Action.ExecAction))) - throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); - v1Actions.Add(action); - SaveV1Actions(); - } - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action)); - return action; - } - - /// Adds an to the task. - /// Path to an executable file. - /// Arguments associated with the command-line operation. This value can be null. - /// - /// Directory that contains either the executable file or the files that are used by the executable file. This value can be null. - /// - /// The bound that was added to the collection. - [NotNull] - public Action.ExecAction Add([NotNull] string path, [CanBeNull] string arguments = null, [CanBeNull] string workingDirectory = null) => - Add(new Action.ExecAction(path, arguments, workingDirectory)); - - /// Adds a new instance to the task. - /// Type of task to be created - /// Specialized instance. - [NotNull] - public Action AddNew(TaskActionType actionType) - { - if (Count >= MaxActions) - throw new ArgumentOutOfRangeException(nameof(actionType), "A maximum of 32 actions is allowed within a single task."); - if (v1Task != null) - { - if (!SupportV1Conversion && (v1Actions.Count >= 1 || actionType != TaskActionType.Execute)) - throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); - return Action.CreateAction(v1Task); - } - return Action.CreateAction(v2Coll.Create(actionType)); - } - - /// Adds a collection of actions to the end of the . - /// - /// The actions to be added to the end of the . The collection itself cannot be null and cannot - /// contain null elements. - /// - /// is null. - public void AddRange([ItemNotNull, NotNull] IEnumerable actions) - { - if (actions == null) - throw new ArgumentNullException(nameof(actions)); - if (v1Task != null) - { - var list = new List(actions); - var at = list.Count == 1 && list[0].ActionType == TaskActionType.Execute; - if (!SupportV1Conversion && ((v1Actions.Count + list.Count) > 1 || !at)) - throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); - v1Actions.AddRange(actions); - SaveV1Actions(); - } - else - { - foreach (var item in actions) - Add(item); - } - } - - /// Clears all actions from the task. - public void Clear() - { - if (v2Coll != null) - v2Coll.Clear(); - else - { - v1Actions.Clear(); - SaveV1Actions(); - } - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - /// Determines whether the contains a specific value. - /// The object to locate in the . - /// true if is found in the ; otherwise, false. - public bool Contains([NotNull] Action item) => Find(a => a.Equals(item)) != null; - - /// Determines whether the specified action type is contained in this collection. - /// Type of the action. - /// true if the specified action type is contained in this collection; otherwise, false. - public bool ContainsType(Type actionType) => Find(a => a.GetType() == actionType) != null; - - /// - /// Copies the elements of the to an array of , starting at a particular index. - /// - /// - /// The array that is the destination of the elements copied from . The array must have zero-based indexing. - /// - /// The zero-based index in array at which copying begins. - public void CopyTo(Action[] array, int arrayIndex) => CopyTo(0, array, arrayIndex, Count); - - /// - /// Copies the elements of the to an array, starting at a particular array index. - /// - /// The zero-based index in the source at which copying begins. - /// - /// The array that is the destination of the elements copied from . The array must have zero-based indexing. - /// - /// The zero-based index in array at which copying begins. - /// The number of elements to copy. - /// is null. - /// is less than 0. - /// - /// The number of elements in the source is greater than the available space from to the end of the destination . - /// - public void CopyTo(int index, [NotNull] Action[] array, int arrayIndex, int count) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index)); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - if (count < 0 || count > (Count - index)) - throw new ArgumentOutOfRangeException(nameof(count)); - if ((Count - index) > (array.Length - arrayIndex)) - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - for (var i = 0; i < count; i++) - array[arrayIndex + i] = (Action)this[index + i].Clone(); - } - - /// Releases all resources used by this class. - public void Dispose() - { - v1Task = null; - v2Def = null; - if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); - } - - /// - /// Searches for an that matches the conditions defined by the specified predicate, and returns the first - /// occurrence within the entire collection. - /// - /// - /// The delegate that defines the conditions of the to search for. - /// - /// - /// The first that matches the conditions defined by the specified predicate, if found; otherwise, null. - /// - public Action Find(Predicate match) - { - if (match == null) - throw new ArgumentNullException(nameof(match)); - foreach (var item in this) - if (match(item)) return item; - return null; - } - - /// - /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based - /// index of the first occurrence within the collection that starts at the specified index and contains the specified number of elements. - /// - /// The zero-based starting index of the search. - /// The number of elements in the collection to search. - /// The delegate that defines the conditions of the element to search for. - /// - /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. - /// - public int FindIndexOf(int startIndex, int count, [NotNull] Predicate match) - { - if (startIndex < 0 || startIndex >= Count) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - if (startIndex + count > Count) - throw new ArgumentOutOfRangeException(nameof(count)); - if (match == null) - throw new ArgumentNullException(nameof(match)); - for (var i = startIndex; i < startIndex + count; i++) - if (match(this[i])) return i; - return -1; - } - - /// - /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based - /// index of the first occurrence within the collection. - /// - /// The delegate that defines the conditions of the element to search for. - /// - /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. - /// - public int FindIndexOf([NotNull] Predicate match) => FindIndexOf(0, Count, match); - - /// Retrieves an enumeration of each of the actions. - /// - /// Returns an object that implements the interface and that can iterate through the - /// objects within the . - /// - public IEnumerator GetEnumerator() - { - if (v2Coll != null) - return new ComEnumerator(() => v2Coll.Count, i => v2Coll[i], Action.CreateAction); - return v1Actions.GetEnumerator(); - } - - /// Determines the index of a specific item in the . - /// The object to locate in the . - /// The index of if found in the list; otherwise, -1. - public int IndexOf(Action item) => FindIndexOf(a => a.Equals(item)); - - /// Determines the index of a specific item in the . - /// The id ( ) of the action to be retrieved. - /// The index of if found in the list; otherwise, -1. - public int IndexOf(string actionId) - { - if (string.IsNullOrEmpty(actionId)) - throw new ArgumentNullException(nameof(actionId)); - return FindIndexOf(a => string.Equals(a.Id, actionId)); - } - - /// Inserts an action at the specified index. - /// The zero-based index at which action should be inserted. - /// The action to insert into the list. - public void Insert(int index, [NotNull] Action action) - { - if (action == null) - throw new ArgumentNullException(nameof(action)); - if (index < 0 || index > Count) - throw new ArgumentOutOfRangeException(nameof(index)); - - if (v2Coll != null) - { - var pushItems = new Action[Count - index]; - if (Count != index) - { - CopyTo(index, pushItems, 0, Count - index); - for (var j = Count - 1; j >= index; j--) - RemoveAt(j); - } - Add(action); - if (Count != index) - for (var k = 0; k < pushItems.Length; k++) - Add(pushItems[k]); - } - else - { - if (!SupportV1Conversion && (index > 0 || !(action is Action.ExecAction))) - throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); - v1Actions.Insert(index, action); - SaveV1Actions(); - } - - if (!inV2set) - { - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action, index)); - } - } - - /// Removes the first occurrence of a specific object from the . - /// The object to remove from the . - /// - /// true if was successfully removed from the ; otherwise, false. This method - /// also returns false if is not found in the original . - /// - public bool Remove([NotNull] Action item) - { - var idx = IndexOf(item); - if (idx != -1) - { - try - { - RemoveAt(idx); - return true; - } - catch { } - } - return false; - } - - /// Removes the action at a specified index. - /// Index of action to remove. - /// Index out of range. - public void RemoveAt(int index) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index), index, "Failed to remove action. Index out of range."); - var item = this[index].Clone(); - if (v2Coll != null) - v2Coll.Remove(++index); - else - { - v1Actions.RemoveAt(index); - SaveV1Actions(); - } - if (!inV2set) - { - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); - } - } - - /// Copies the elements of the to a new array. - /// An array containing copies of the elements of the . - [NotNull, ItemNotNull] - public Action[] ToArray() - { - var ret = new Action[Count]; - CopyTo(ret, 0); - return ret; - } - - /// Returns a that represents the actions in this collection. - /// A that represents the actions in this collection. - public override string ToString() - { - if (Count == 1) - return this[0].ToString(); - if (Count > 1) - return Properties.Resources.MultipleActions; - return string.Empty; - } - - void ICollection.Add(Action item) => Add(item); - - int IList.Add(object value) - { - Add((Action)value); - return Count - 1; - } - - bool IList.Contains(object value) => Contains((Action)value); - - void ICollection.CopyTo(Array array, int index) - { - if (array != null && array.Rank != 1) - throw new RankException("Multi-dimensional arrays are not supported."); - var src = new Action[Count]; - CopyTo(src, 0); - Array.Copy(src, 0, array, index, Count); - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - int IList.IndexOf(object value) => IndexOf((Action)value); - - void IList.Insert(int index, object value) => Insert(index, (Action)value); - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); - Context = reader.GetAttribute("Context"); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - var a = Action.CreateAction(Action.TryParse(reader.LocalName == "Exec" ? "Execute" : reader.LocalName, TaskActionType.Execute)); - XmlSerializationHelper.ReadObject(reader, a); - Add(a); - } - reader.ReadEndElement(); - } - - void IList.Remove(object value) => Remove((Action)value); - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) - { - // TODO:FIX if (!string.IsNullOrEmpty(Context)) writer.WriteAttributeString("Context", Context); - foreach (var item in this) - XmlSerializationHelper.WriteObject(writer, item); - } - - internal void ConvertUnsupportedActions() - { - if (TaskService.LibraryVersion.Minor > 3 && SupportV2Conversion) - { - for (var i = 0; i < Count; i++) - { - var action = this[i]; - var bindable = action as IBindAsExecAction; - if (bindable != null && !(action is Action.ComHandlerAction)) - this[i] = Action.ExecAction.ConvertToPowerShellAction(action); - } - } - } - - private List GetV1Actions() - { - var ret = new List(); - if (v1Task != null && v1Task.GetDataItem("ActionType") != "EMPTY") - { - var exec = new Action.ExecAction(v1Task); - var items = exec.ParsePowerShellItems(); - if (items != null) - { - if (items.Length == 2 && items[0] == "MULTIPLE") - { - PowerShellConversion |= PowerShellActionPlatformOption.Version1; - var mc = System.Text.RegularExpressions.Regex.Matches(items[1], @"<# (?\w+):(?\w+) #>\s*(?[^<#]*)\s*"); - foreach (System.Text.RegularExpressions.Match ms in mc) - { - var a = Action.ActionFromScript(ms.Groups["t"].Value, ms.Groups["c"].Value); - if (a != null) - { - if (ms.Groups["id"].Value != "NO_ID") - a.Id = ms.Groups["id"].Value; - ret.Add(a); - } - } - } - else - ret.Add(Action.ExecAction.ConvertFromPowerShellAction(exec)); - } - else if (!string.IsNullOrEmpty(exec.Path)) - { - ret.Add(exec); - } - } - return ret; - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - private void SaveV1Actions() - { - if (v1Task == null) - throw new ArgumentNullException(nameof(v1Task)); - if (v1Actions.Count == 0) - { - v1Task.SetApplicationName(string.Empty); - v1Task.SetParameters(string.Empty); - v1Task.SetWorkingDirectory(string.Empty); - v1Task.SetDataItem("ActionId", null); - v1Task.SetDataItem("ActionType", "EMPTY"); - } - else if (v1Actions.Count == 1) - { - if (!SupportV1Conversion && v1Actions[0].ActionType != TaskActionType.Execute) - throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); - v1Task.SetDataItem("ActionType", null); - v1Actions[0].Bind(v1Task); - } - else - { - if (!SupportV1Conversion) - throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); - // Build list of internal PowerShell scripts - var sb = new System.Text.StringBuilder(); - foreach (var item in v1Actions) - sb.Append($"<# {item.Id ?? "NO_ID"}:{item.ActionType} #> {item.GetPowerShellCommand()} "); - - // Build and save PS ExecAction - var ea = Action.ExecAction.CreatePowerShellAction("MULTIPLE", sb.ToString()); - ea.Bind(v1Task); - v1Task.SetDataItem("ActionId", null); - v1Task.SetDataItem("ActionType", "MULTIPLE"); - } - } - - private void UnconvertUnsupportedActions() - { - if (TaskService.LibraryVersion.Minor > 3) - { - for (var i = 0; i < Count; i++) - { - var action = this[i] as Action.ExecAction; - if (action != null) - { - var newAction = Action.ConvertFromPowerShellAction(action); - if (newAction != null) - this[i] = newAction; - } - } - } - } - } + /// Options for when to convert actions to PowerShell equivalents. + [Flags] + public enum PowerShellActionPlatformOption + { + /// + /// Never convert any actions to PowerShell. This will force exceptions to be thrown when unsupported actions our action quantities + /// are found. + /// + Never = 0, + + /// + /// Convert actions under Version 1 of the library (Windows XP or Windows Server 2003 and earlier). This option supports multiple + /// actions of all types. If not specified, only a single is supported. Developer must ensure that + /// PowerShell v2 or higher is installed on the target computer. + /// + Version1 = 1, + + /// + /// Convert all and references to their PowerShell equivalents on systems + /// on or after Windows 8 / Server 2012. + /// + Version2 = 2, + + /// Convert all actions regardless of version or operating system. + All = 3 + } + + /// Collection that contains the actions that are performed by the task. + [XmlRoot("Actions", Namespace = TaskDefinition.tns, IsNullable = false)] + [PublicAPI] + public sealed class ActionCollection : IList, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged + { + internal const int MaxActions = 32; + private const string IndexerName = "Item[]"; + private static readonly string psV2IdRegex = $"(?:; )?{nameof(PowerShellConversion)}=(?0|1)"; + private bool inV2set; + private PowerShellActionPlatformOption psConvert = PowerShellActionPlatformOption.Version2; + private readonly List v1Actions; + private ITask v1Task; + private readonly IActionCollection v2Coll; + private ITaskDefinition v2Def; + + internal ActionCollection([NotNull] ITask task) + { + v1Task = task; + v1Actions = GetV1Actions(); + PowerShellConversion = Action.TryParse(v1Task.GetDataItem(nameof(PowerShellConversion)), psConvert | PowerShellActionPlatformOption.Version2); + } + + internal ActionCollection([NotNull] ITaskDefinition iTaskDef) + { + v2Def = iTaskDef; + v2Coll = iTaskDef.Actions; + System.Text.RegularExpressions.Match match; + if (iTaskDef.Data != null && (match = System.Text.RegularExpressions.Regex.Match(iTaskDef.Data, psV2IdRegex)).Success) + { + var on = false; + try { on = bool.Parse(match.Groups["v"].Value); } catch { try { on = int.Parse(match.Groups["v"].Value) == 1; } catch { } } + if (on) + psConvert |= PowerShellActionPlatformOption.Version2; + else + psConvert &= ~PowerShellActionPlatformOption.Version2; + } + UnconvertUnsupportedActions(); + } + + /// Occurs when a collection changes. + public event NotifyCollectionChangedEventHandler CollectionChanged; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets or sets the identifier of the principal for the task. + [XmlAttribute] + [DefaultValue(null)] + [CanBeNull] + public string Context + { + get + { + if (v2Coll != null) + return v2Coll.Context; + return v1Task.GetDataItem("ActionCollectionContext"); + } + set + { + if (v2Coll != null) + v2Coll.Context = value; + else + v1Task.SetDataItem("ActionCollectionContext", value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the systems under which unsupported actions will be converted to PowerShell instances. + /// + /// The PowerShell platform options. + /// + /// This property will affect how many actions are physically stored in the system and is tied to the version of Task Scheduler. + /// + /// If set to , then no actions will ever be converted to PowerShell. This will + /// force exceptions to be thrown when unsupported actions our action quantities are found. + /// + /// + /// If set to , then actions will be converted only under Version 1 of the + /// library (Windows XP or Windows Server 2003 and earlier). This option supports multiple actions of all types. If not specified, + /// only a single is supported. Developer must ensure that PowerShell v2 or higher is installed on the + /// target computer. + /// + /// + /// If set to (which is the default value), then and references will be converted to their PowerShell equivalents on systems + /// on or after Windows 8 / Server 2012. + /// + /// + /// If set to , then any actions not supported by the Task Scheduler version will be + /// converted to PowerShell. + /// + /// + [DefaultValue(typeof(PowerShellActionPlatformOption), "Version2")] + public PowerShellActionPlatformOption PowerShellConversion + { + get => psConvert; + set + { + if (psConvert != value) + { + psConvert = value; + if (v1Task != null) + v1Task.SetDataItem(nameof(PowerShellConversion), value.ToString()); + if (v2Def != null) + { + if (!string.IsNullOrEmpty(v2Def.Data)) + v2Def.Data = System.Text.RegularExpressions.Regex.Replace(v2Def.Data, psV2IdRegex, ""); + if (!SupportV2Conversion) + v2Def.Data = string.Format("{0}; {1}=0", v2Def.Data, nameof(PowerShellConversion)); + } + OnNotifyPropertyChanged(); + } + } + } + + /// Gets or sets an XML-formatted version of the collection. + public string XmlText + { + get + { + if (v2Coll != null) + return v2Coll.XmlText; + return XmlSerializationHelper.WriteObjectToXmlText(this); + } + set + { + if (v2Coll != null) + v2Coll.XmlText = value; + else + XmlSerializationHelper.ReadObjectFromXmlText(value, this); + OnNotifyPropertyChanged(); + } + } + + /// Gets the number of actions in the collection. + public int Count => (v2Coll != null) ? v2Coll.Count : v1Actions.Count; + + bool IList.IsFixedSize => false; + bool ICollection.IsReadOnly => false; + bool IList.IsReadOnly => false; + bool ICollection.IsSynchronized => false; + + object ICollection.SyncRoot => this; + private bool SupportV1Conversion => (PowerShellConversion & PowerShellActionPlatformOption.Version1) != 0; + + private bool SupportV2Conversion => (PowerShellConversion & PowerShellActionPlatformOption.Version2) != 0; + + /// Gets or sets a specified action from the collection. + /// The . + /// The id ( ) of the action to be retrieved. + /// Specialized instance. + /// + /// + /// + /// Mismatching Id for action and lookup. + [NotNull] + public Action this[string actionId] + { + get + { + if (string.IsNullOrEmpty(actionId)) + throw new ArgumentNullException(nameof(actionId)); + var t = Find(a => string.Equals(a.Id, actionId)); + if (t != null) + return t; + throw new ArgumentOutOfRangeException(nameof(actionId)); + } + set + { + if (value == null) + throw new NullReferenceException(); + if (string.IsNullOrEmpty(actionId)) + throw new ArgumentNullException(nameof(actionId)); + var index = IndexOf(actionId); + value.Id = actionId; + if (index >= 0) + this[index] = value; + else + Add(value); + } + } + + /// Gets or sets a an action at the specified index. + /// The zero-based index of the action to get or set. + [NotNull] + public Action this[int index] + { + get + { + if (v2Coll != null) + return Action.CreateAction(v2Coll[++index]); + if (v1Task != null) + { + if (SupportV1Conversion) + return v1Actions[index]; + else + { + if (index == 0) + return v1Actions[0]; + } + } + throw new ArgumentOutOfRangeException(); + } + set + { + if (index < 0 || Count <= index) + throw new ArgumentOutOfRangeException(nameof(index), index, "Index is not a valid index in the ActionCollection"); + var orig = this[index].Clone(); + if (v2Coll != null) + { + inV2set = true; + try + { + Insert(index, value); + RemoveAt(index + 1); + } + finally + { + inV2set = false; + } + } + else + { + v1Actions[index] = value; + SaveV1Actions(); + } + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index)); + } + } + + object IList.this[int index] + { + get => this[index]; + set => this[index] = (Action)value; + } + + /// Adds an action to the task. + /// A type derived from . + /// A derived class. + /// The bound that was added to the collection. + [NotNull] + public TAction Add([NotNull] TAction action) where TAction : Action + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + if (v2Def != null) + action.Bind(v2Def); + else + { + if (!SupportV1Conversion && (v1Actions.Count >= 1 || !(action is Action.ExecAction))) + throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); + v1Actions.Add(action); + SaveV1Actions(); + } + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action)); + return action; + } + + /// Adds an to the task. + /// Path to an executable file. + /// Arguments associated with the command-line operation. This value can be null. + /// + /// Directory that contains either the executable file or the files that are used by the executable file. This value can be null. + /// + /// The bound that was added to the collection. + [NotNull] + public Action.ExecAction Add([NotNull] string path, [CanBeNull] string arguments = null, [CanBeNull] string workingDirectory = null) => + Add(new Action.ExecAction(path, arguments, workingDirectory)); + + /// Adds a new instance to the task. + /// Type of task to be created + /// Specialized instance. + [NotNull] + public Action AddNew(TaskActionType actionType) + { + if (Count >= MaxActions) + throw new ArgumentOutOfRangeException(nameof(actionType), "A maximum of 32 actions is allowed within a single task."); + if (v1Task != null) + { + if (!SupportV1Conversion && (v1Actions.Count >= 1 || actionType != TaskActionType.Execute)) + throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); + return Action.CreateAction(v1Task); + } + return Action.CreateAction(v2Coll.Create(actionType)); + } + + /// Adds a collection of actions to the end of the . + /// + /// The actions to be added to the end of the . The collection itself cannot be null and cannot + /// contain null elements. + /// + /// is null. + public void AddRange([ItemNotNull, NotNull] IEnumerable actions) + { + if (actions == null) + throw new ArgumentNullException(nameof(actions)); + if (v1Task != null) + { + var list = new List(actions); + var at = list.Count == 1 && list[0].ActionType == TaskActionType.Execute; + if (!SupportV1Conversion && ((v1Actions.Count + list.Count) > 1 || !at)) + throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); + v1Actions.AddRange(actions); + SaveV1Actions(); + } + else + { + foreach (var item in actions) + Add(item); + } + } + + /// Clears all actions from the task. + public void Clear() + { + if (v2Coll != null) + v2Coll.Clear(); + else + { + v1Actions.Clear(); + SaveV1Actions(); + } + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + /// Determines whether the contains a specific value. + /// The object to locate in the . + /// true if is found in the ; otherwise, false. + public bool Contains([NotNull] Action item) => Find(a => a.Equals(item)) != null; + + /// Determines whether the specified action type is contained in this collection. + /// Type of the action. + /// true if the specified action type is contained in this collection; otherwise, false. + public bool ContainsType(Type actionType) => Find(a => a.GetType() == actionType) != null; + + /// + /// Copies the elements of the to an array of , starting at a particular index. + /// + /// + /// The array that is the destination of the elements copied from . The array must have zero-based indexing. + /// + /// The zero-based index in array at which copying begins. + public void CopyTo(Action[] array, int arrayIndex) => CopyTo(0, array, arrayIndex, Count); + + /// + /// Copies the elements of the to an array, starting at a particular array index. + /// + /// The zero-based index in the source at which copying begins. + /// + /// The array that is the destination of the elements copied from . The array must have zero-based indexing. + /// + /// The zero-based index in array at which copying begins. + /// The number of elements to copy. + /// is null. + /// is less than 0. + /// + /// The number of elements in the source is greater than the available space from to the end of the destination . + /// + public void CopyTo(int index, [NotNull] Action[] array, int arrayIndex, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException(nameof(index)); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + if (count < 0 || count > (Count - index)) + throw new ArgumentOutOfRangeException(nameof(count)); + if ((Count - index) > (array.Length - arrayIndex)) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + for (var i = 0; i < count; i++) + array[arrayIndex + i] = (Action)this[index + i].Clone(); + } + + /// Releases all resources used by this class. + public void Dispose() + { + v1Task = null; + v2Def = null; + if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); + } + + /// + /// Searches for an that matches the conditions defined by the specified predicate, and returns the first + /// occurrence within the entire collection. + /// + /// + /// The delegate that defines the conditions of the to search for. + /// + /// + /// The first that matches the conditions defined by the specified predicate, if found; otherwise, null. + /// + public Action Find(Predicate match) + { + if (match == null) + throw new ArgumentNullException(nameof(match)); + foreach (var item in this) + if (match(item)) return item; + return null; + } + + /// + /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based + /// index of the first occurrence within the collection that starts at the specified index and contains the specified number of elements. + /// + /// The zero-based starting index of the search. + /// The number of elements in the collection to search. + /// The delegate that defines the conditions of the element to search for. + /// + /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. + /// + public int FindIndexOf(int startIndex, int count, [NotNull] Predicate match) + { + if (startIndex < 0 || startIndex >= Count) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + if (startIndex + count > Count) + throw new ArgumentOutOfRangeException(nameof(count)); + if (match == null) + throw new ArgumentNullException(nameof(match)); + for (var i = startIndex; i < startIndex + count; i++) + if (match(this[i])) return i; + return -1; + } + + /// + /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based + /// index of the first occurrence within the collection. + /// + /// The delegate that defines the conditions of the element to search for. + /// + /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. + /// + public int FindIndexOf([NotNull] Predicate match) => FindIndexOf(0, Count, match); + + /// Retrieves an enumeration of each of the actions. + /// + /// Returns an object that implements the interface and that can iterate through the + /// objects within the . + /// + public IEnumerator GetEnumerator() + { + if (v2Coll != null) + return new ComEnumerator(() => v2Coll.Count, i => v2Coll[i], Action.CreateAction); + return v1Actions.GetEnumerator(); + } + + /// Determines the index of a specific item in the . + /// The object to locate in the . + /// The index of if found in the list; otherwise, -1. + public int IndexOf(Action item) => FindIndexOf(a => a.Equals(item)); + + /// Determines the index of a specific item in the . + /// The id ( ) of the action to be retrieved. + /// The index of if found in the list; otherwise, -1. + public int IndexOf(string actionId) + { + if (string.IsNullOrEmpty(actionId)) + throw new ArgumentNullException(nameof(actionId)); + return FindIndexOf(a => string.Equals(a.Id, actionId)); + } + + /// Inserts an action at the specified index. + /// The zero-based index at which action should be inserted. + /// The action to insert into the list. + public void Insert(int index, [NotNull] Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + if (index < 0 || index > Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + if (v2Coll != null) + { + var pushItems = new Action[Count - index]; + if (Count != index) + { + CopyTo(index, pushItems, 0, Count - index); + for (var j = Count - 1; j >= index; j--) + RemoveAt(j); + } + Add(action); + if (Count != index) + for (var k = 0; k < pushItems.Length; k++) + Add(pushItems[k]); + } + else + { + if (!SupportV1Conversion && (index > 0 || !(action is Action.ExecAction))) + throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); + v1Actions.Insert(index, action); + SaveV1Actions(); + } + + if (!inV2set) + { + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action, index)); + } + } + + /// Removes the first occurrence of a specific object from the . + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method + /// also returns false if is not found in the original . + /// + public bool Remove([NotNull] Action item) + { + var idx = IndexOf(item); + if (idx != -1) + { + try + { + RemoveAt(idx); + return true; + } + catch { } + } + return false; + } + + /// Removes the action at a specified index. + /// Index of action to remove. + /// Index out of range. + public void RemoveAt(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException(nameof(index), index, "Failed to remove action. Index out of range."); + var item = this[index].Clone(); + if (v2Coll != null) + v2Coll.Remove(++index); + else + { + v1Actions.RemoveAt(index); + SaveV1Actions(); + } + if (!inV2set) + { + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); + } + } + + /// Copies the elements of the to a new array. + /// An array containing copies of the elements of the . + [NotNull, ItemNotNull] + public Action[] ToArray() + { + var ret = new Action[Count]; + CopyTo(ret, 0); + return ret; + } + + /// Returns a that represents the actions in this collection. + /// A that represents the actions in this collection. + public override string ToString() + { + if (Count == 1) + return this[0].ToString(); + if (Count > 1) + return Properties.Resources.MultipleActions; + return string.Empty; + } + + void ICollection.Add(Action item) => Add(item); + + int IList.Add(object value) + { + Add((Action)value); + return Count - 1; + } + + bool IList.Contains(object value) => Contains((Action)value); + + void ICollection.CopyTo(Array array, int index) + { + if (array != null && array.Rank != 1) + throw new RankException("Multi-dimensional arrays are not supported."); + var src = new Action[Count]; + CopyTo(src, 0); + Array.Copy(src, 0, array, index, Count); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + int IList.IndexOf(object value) => IndexOf((Action)value); + + void IList.Insert(int index, object value) => Insert(index, (Action)value); + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); + Context = reader.GetAttribute("Context"); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + var a = Action.CreateAction(Action.TryParse(reader.LocalName == "Exec" ? "Execute" : reader.LocalName, TaskActionType.Execute)); + XmlSerializationHelper.ReadObject(reader, a); + Add(a); + } + reader.ReadEndElement(); + } + + void IList.Remove(object value) => Remove((Action)value); + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) + { + // TODO:FIX if (!string.IsNullOrEmpty(Context)) writer.WriteAttributeString("Context", Context); + foreach (var item in this) + XmlSerializationHelper.WriteObject(writer, item); + } + + internal void ConvertUnsupportedActions() + { + if (TaskService.LibraryVersion.Minor > 3 && SupportV2Conversion) + { + for (var i = 0; i < Count; i++) + { + var action = this[i]; + var bindable = action as IBindAsExecAction; + if (bindable != null && !(action is Action.ComHandlerAction)) + this[i] = Action.ExecAction.ConvertToPowerShellAction(action); + } + } + } + + private List GetV1Actions() + { + var ret = new List(); + if (v1Task != null && v1Task.GetDataItem("ActionType") != "EMPTY") + { + var exec = new Action.ExecAction(v1Task); + var items = exec.ParsePowerShellItems(); + if (items != null) + { + if (items.Length == 2 && items[0] == "MULTIPLE") + { + PowerShellConversion |= PowerShellActionPlatformOption.Version1; + var mc = System.Text.RegularExpressions.Regex.Matches(items[1], @"<# (?\w+):(?\w+) #>\s*(?[^<#]*)\s*"); + foreach (System.Text.RegularExpressions.Match ms in mc) + { + var a = Action.ActionFromScript(ms.Groups["t"].Value, ms.Groups["c"].Value); + if (a != null) + { + if (ms.Groups["id"].Value != "NO_ID") + a.Id = ms.Groups["id"].Value; + ret.Add(a); + } + } + } + else + ret.Add(Action.ConvertFromPowerShellAction(exec)); + } + else if (!string.IsNullOrEmpty(exec.Path)) + { + ret.Add(exec); + } + } + return ret; + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private void SaveV1Actions() + { + if (v1Task == null) + throw new ArgumentNullException(nameof(v1Task)); + if (v1Actions.Count == 0) + { + v1Task.SetApplicationName(string.Empty); + v1Task.SetParameters(string.Empty); + v1Task.SetWorkingDirectory(string.Empty); + v1Task.SetDataItem("ActionId", null); + v1Task.SetDataItem("ActionType", "EMPTY"); + } + else if (v1Actions.Count == 1) + { + if (!SupportV1Conversion && v1Actions[0].ActionType != TaskActionType.Execute) + throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); + v1Task.SetDataItem("ActionType", null); + v1Actions[0].Bind(v1Task); + } + else + { + if (!SupportV1Conversion) + throw new NotV1SupportedException($"Only a single {nameof(Action.ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value."); + // Build list of internal PowerShell scripts + var sb = new System.Text.StringBuilder(); + foreach (var item in v1Actions) + sb.Append($"<# {item.Id ?? "NO_ID"}:{item.ActionType} #> {item.GetPowerShellCommand()} "); + + // Build and save PS ExecAction + var ea = Action.ExecAction.CreatePowerShellAction("MULTIPLE", sb.ToString()); + ea.Bind(v1Task); + v1Task.SetDataItem("ActionId", null); + v1Task.SetDataItem("ActionType", "MULTIPLE"); + } + } + + private void UnconvertUnsupportedActions() + { + if (TaskService.LibraryVersion.Minor > 3) + { + for (var i = 0; i < Count; i++) + { + var action = this[i] as Action.ExecAction; + if (action != null) + { + var newAction = Action.ConvertFromPowerShellAction(action); + if (newAction != null) + this[i] = newAction; + } + } + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/CultureSwitcher.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/CultureSwitcher.cs index ea584607e..6e1be7e46 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/CultureSwitcher.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/CultureSwitcher.cs @@ -1,27 +1,23 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - internal class CultureSwitcher : IDisposable - { - private readonly System.Globalization.CultureInfo cur, curUI; + internal class CultureSwitcher : IDisposable + { + private readonly System.Globalization.CultureInfo cur, curUI; - public CultureSwitcher([NotNull] System.Globalization.CultureInfo culture) - { - cur = Thread.CurrentThread.CurrentCulture; - curUI = Thread.CurrentThread.CurrentUICulture; - Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = culture; - } + public CultureSwitcher([NotNull] System.Globalization.CultureInfo culture) + { + cur = Thread.CurrentThread.CurrentCulture; + curUI = Thread.CurrentThread.CurrentUICulture; + Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = culture; + } - public void Dispose() - { - Thread.CurrentThread.CurrentCulture = cur; - Thread.CurrentThread.CurrentUICulture = curUI; - } - } + public void Dispose() + { + Thread.CurrentThread.CurrentCulture = cur; + Thread.CurrentThread.CurrentUICulture = curUI; + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumGlobalizer.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumGlobalizer.cs index edfac2ae3..015f9963c 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumGlobalizer.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumGlobalizer.cs @@ -8,72 +8,72 @@ namespace winPEAS.TaskScheduler /// Functions to provide localized strings for enumerated types and values. /// public static class TaskEnumGlobalizer - { - /// - /// Gets a string representing the localized value of the provided enum. - /// - /// The enum value. - /// A localized string, if available. - public static string GetString(object enumValue) - { - switch (enumValue.GetType().Name) - { - case "DaysOfTheWeek": - return GetCultureEquivalentString((DaysOfTheWeek)enumValue); - case "MonthsOfTheYear": - return GetCultureEquivalentString((MonthsOfTheYear)enumValue); - case "TaskTriggerType": - return BuildEnumString("TriggerType", enumValue); - case "WhichWeek": - return BuildEnumString("WW", enumValue); - case "TaskActionType": - return BuildEnumString("ActionType", enumValue); - case "TaskState": - return BuildEnumString("TaskState", enumValue); - } - return enumValue.ToString(); - } + { + /// + /// Gets a string representing the localized value of the provided enum. + /// + /// The enum value. + /// A localized string, if available. + public static string GetString(object enumValue) + { + switch (enumValue.GetType().Name) + { + case "DaysOfTheWeek": + return GetCultureEquivalentString((DaysOfTheWeek)enumValue); + case "MonthsOfTheYear": + return GetCultureEquivalentString((MonthsOfTheYear)enumValue); + case "TaskTriggerType": + return BuildEnumString("TriggerType", enumValue); + case "WhichWeek": + return BuildEnumString("WW", enumValue); + case "TaskActionType": + return BuildEnumString("ActionType", enumValue); + case "TaskState": + return BuildEnumString("TaskState", enumValue); + } + return enumValue.ToString(); + } - private static string GetCultureEquivalentString(DaysOfTheWeek val) - { - if (val == DaysOfTheWeek.AllDays) - return Properties.Resources.DOWAllDays; + private static string GetCultureEquivalentString(DaysOfTheWeek val) + { + if (val == DaysOfTheWeek.AllDays) + return Properties.Resources.DOWAllDays; - var s = new List(7); - var vals = Enum.GetValues(val.GetType()); - for (var i = 0; i < vals.Length - 1; i++) - { - if ((val & (DaysOfTheWeek)vals.GetValue(i)) > 0) - s.Add(DateTimeFormatInfo.CurrentInfo.GetDayName((DayOfWeek)i)); - } + var s = new List(7); + var vals = Enum.GetValues(val.GetType()); + for (var i = 0; i < vals.Length - 1; i++) + { + if ((val & (DaysOfTheWeek)vals.GetValue(i)) > 0) + s.Add(DateTimeFormatInfo.CurrentInfo.GetDayName((DayOfWeek)i)); + } - return string.Join(Properties.Resources.ListSeparator, s.ToArray()); - } + return string.Join(Properties.Resources.ListSeparator, s.ToArray()); + } - private static string GetCultureEquivalentString(MonthsOfTheYear val) - { - if (val == MonthsOfTheYear.AllMonths) - return Properties.Resources.MOYAllMonths; + private static string GetCultureEquivalentString(MonthsOfTheYear val) + { + if (val == MonthsOfTheYear.AllMonths) + return Properties.Resources.MOYAllMonths; - var s = new List(12); - var vals = Enum.GetValues(val.GetType()); - for (var i = 0; i < vals.Length - 1; i++) - { - if ((val & (MonthsOfTheYear)vals.GetValue(i)) > 0) - s.Add(DateTimeFormatInfo.CurrentInfo.GetMonthName(i + 1)); - } + var s = new List(12); + var vals = Enum.GetValues(val.GetType()); + for (var i = 0; i < vals.Length - 1; i++) + { + if ((val & (MonthsOfTheYear)vals.GetValue(i)) > 0) + s.Add(DateTimeFormatInfo.CurrentInfo.GetMonthName(i + 1)); + } - return string.Join(Properties.Resources.ListSeparator, s.ToArray()); - } + return string.Join(Properties.Resources.ListSeparator, s.ToArray()); + } - private static string BuildEnumString(string preface, object enumValue) - { - var vals = enumValue.ToString().Split(new[] { ", " }, StringSplitOptions.None); - if (vals.Length == 0) - return string.Empty; - for (var i = 0; i < vals.Length; i++) - vals[i] = Properties.Resources.ResourceManager.GetString(preface + vals[i]); - return string.Join(Properties.Resources.ListSeparator, vals); - } - } + private static string BuildEnumString(string preface, object enumValue) + { + var vals = enumValue.ToString().Split(new[] { ", " }, StringSplitOptions.None); + if (vals.Length == 0) + return string.Empty; + for (var i = 0; i < vals.Length; i++) + vals[i] = Properties.Resources.ResourceManager.GetString(preface + vals[i]); + return string.Join(Properties.Resources.ListSeparator, vals); + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumUtil.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumUtil.cs index a30ca439e..93d7b4b18 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumUtil.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/EnumUtil.cs @@ -1,150 +1,147 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - internal static class EnumUtil - { - public static void CheckIsEnum(bool checkHasFlags = false) - { - if (!typeof(T).IsEnum) - throw new ArgumentException($"Type '{typeof(T).FullName}' is not an enum"); - if (checkHasFlags && !IsFlags()) - throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute"); - } + internal static class EnumUtil + { + public static void CheckIsEnum(bool checkHasFlags = false) + { + if (!typeof(T).IsEnum) + throw new ArgumentException($"Type '{typeof(T).FullName}' is not an enum"); + if (checkHasFlags && !IsFlags()) + throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute"); + } - public static bool IsFlags() => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)); + public static bool IsFlags() => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)); - public static void CheckHasValue(T value, string argName = null) - { - CheckIsEnum(); - if (IsFlags()) - { - var allFlags = 0L; - foreach (T flag in Enum.GetValues(typeof(T))) - allFlags |= Convert.ToInt64(flag); - if ((allFlags & Convert.ToInt64(value)) != 0L) - return; - } - else if (Enum.IsDefined(typeof(T), value)) - return; - throw new InvalidEnumArgumentException(argName ?? "value", Convert.ToInt32(value), typeof(T)); - } + public static void CheckHasValue(T value, string argName = null) + { + CheckIsEnum(); + if (IsFlags()) + { + var allFlags = 0L; + foreach (T flag in Enum.GetValues(typeof(T))) + allFlags |= Convert.ToInt64(flag); + if ((allFlags & Convert.ToInt64(value)) != 0L) + return; + } + else if (Enum.IsDefined(typeof(T), value)) + return; + throw new InvalidEnumArgumentException(argName ?? "value", Convert.ToInt32(value), typeof(T)); + } - public static byte BitPosition(this T flags) where T : struct, IConvertible - { - CheckIsEnum(true); - var flagValue = Convert.ToInt64(flags); - if (flagValue == 0) throw new ArgumentException("The flag value is zero and has no bit position."); - var r = Math.Log(flagValue, 2); - if (r % 1 > 0) throw new ArithmeticException("The flag value has more than a single bit set."); - return Convert.ToByte(r); - } + public static byte BitPosition(this T flags) where T : struct, IConvertible + { + CheckIsEnum(true); + var flagValue = Convert.ToInt64(flags); + if (flagValue == 0) throw new ArgumentException("The flag value is zero and has no bit position."); + var r = Math.Log(flagValue, 2); + if (r % 1 > 0) throw new ArithmeticException("The flag value has more than a single bit set."); + return Convert.ToByte(r); + } - public static bool IsFlagSet(this T flags, T flag) where T : struct, IConvertible - { - CheckIsEnum(true); - var flagValue = Convert.ToInt64(flag); - return (Convert.ToInt64(flags) & flagValue) == flagValue; - } + public static bool IsFlagSet(this T flags, T flag) where T : struct, IConvertible + { + CheckIsEnum(true); + var flagValue = Convert.ToInt64(flag); + return (Convert.ToInt64(flags) & flagValue) == flagValue; + } - public static bool IsValidFlagValue(this T flags) where T : struct, IConvertible - { - CheckIsEnum(true); - var found = 0L; - foreach (T flag in Enum.GetValues(typeof(T))) - { - if (flags.IsFlagSet(flag)) - found |= Convert.ToInt64(flag); - } - return found == Convert.ToInt64(flags); - } + public static bool IsValidFlagValue(this T flags) where T : struct, IConvertible + { + CheckIsEnum(true); + var found = 0L; + foreach (T flag in Enum.GetValues(typeof(T))) + { + if (flags.IsFlagSet(flag)) + found |= Convert.ToInt64(flag); + } + return found == Convert.ToInt64(flags); + } - public static void SetFlags(ref T flags, T flag, bool set = true) where T : struct, IConvertible - { - CheckIsEnum(true); - var flagsValue = Convert.ToInt64(flags); - var flagValue = Convert.ToInt64(flag); - if (set) - flagsValue |= flagValue; - else - flagsValue &= (~flagValue); - flags = (T)Enum.ToObject(typeof(T), flagsValue); - } + public static void SetFlags(ref T flags, T flag, bool set = true) where T : struct, IConvertible + { + CheckIsEnum(true); + var flagsValue = Convert.ToInt64(flags); + var flagValue = Convert.ToInt64(flag); + if (set) + flagsValue |= flagValue; + else + flagsValue &= (~flagValue); + flags = (T)Enum.ToObject(typeof(T), flagsValue); + } - public static T SetFlags(this T flags, T flag, bool set = true) where T : struct, IConvertible - { - var ret = flags; - SetFlags(ref ret, flag, set); - return ret; - } + public static T SetFlags(this T flags, T flag, bool set = true) where T : struct, IConvertible + { + var ret = flags; + SetFlags(ref ret, flag, set); + return ret; + } - public static T ClearFlags(this T flags, T flag) where T : struct, IConvertible => flags.SetFlags(flag, false); + public static T ClearFlags(this T flags, T flag) where T : struct, IConvertible => flags.SetFlags(flag, false); - public static IEnumerable GetFlags(this T value) where T : struct, IConvertible - { - CheckIsEnum(true); - foreach (T flag in Enum.GetValues(typeof(T))) - { - if (value.IsFlagSet(flag)) - yield return flag; - } - } + public static IEnumerable GetFlags(this T value) where T : struct, IConvertible + { + CheckIsEnum(true); + foreach (T flag in Enum.GetValues(typeof(T))) + { + if (value.IsFlagSet(flag)) + yield return flag; + } + } - public static T CombineFlags(this IEnumerable flags) where T : struct, IConvertible - { - CheckIsEnum(true); - long lValue = 0; - foreach (var flag in flags) - { - var lFlag = Convert.ToInt64(flag); - lValue |= lFlag; - } - return (T)Enum.ToObject(typeof(T), lValue); - } + public static T CombineFlags(this IEnumerable flags) where T : struct, IConvertible + { + CheckIsEnum(true); + long lValue = 0; + foreach (var flag in flags) + { + var lFlag = Convert.ToInt64(flag); + lValue |= lFlag; + } + return (T)Enum.ToObject(typeof(T), lValue); + } - public static string GetDescription(this T value) where T : struct, IConvertible - { - CheckIsEnum(); - var name = Enum.GetName(typeof(T), value); - if (name != null) - { - var field = typeof(T).GetField(name); - if (field != null) - { - var attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; - if (attr != null) - { - return attr.Description; - } - } - } - return null; - } + public static string GetDescription(this T value) where T : struct, IConvertible + { + CheckIsEnum(); + var name = Enum.GetName(typeof(T), value); + if (name != null) + { + var field = typeof(T).GetField(name); + if (field != null) + { + var attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; + if (attr != null) + { + return attr.Description; + } + } + } + return null; + } - /// - /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object or returns the value of . If is undefined, it returns the first declared item in the enumerated type. - /// - /// The enumeration type to which to convert . - /// The string representation of the enumeration name or underlying value to convert. - /// true to ignore case; false to consider case. - /// The default value. - /// An object of type whose value is represented by value. - public static TEnum TryParse(string value, bool ignoreCase = false, TEnum defaultVal = default(TEnum)) where TEnum : struct, IConvertible - { - CheckIsEnum(); - try { return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase); } catch { } - if (!Enum.IsDefined(typeof(TEnum), defaultVal)) - { - var v = Enum.GetValues(typeof(TEnum)); - if (v != null && v.Length > 0) - return (TEnum)v.GetValue(0); - } - return defaultVal; - } - } + /// + /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object or returns the value of . If is undefined, it returns the first declared item in the enumerated type. + /// + /// The enumeration type to which to convert . + /// The string representation of the enumeration name or underlying value to convert. + /// true to ignore case; false to consider case. + /// The default value. + /// An object of type whose value is represented by value. + public static TEnum TryParse(string value, bool ignoreCase = false, TEnum defaultVal = default(TEnum)) where TEnum : struct, IConvertible + { + CheckIsEnum(); + try { return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase); } catch { } + if (!Enum.IsDefined(typeof(TEnum), defaultVal)) + { + var v = Enum.GetValues(typeof(TEnum)); + if (v != null && v.Length > 0) + return (TEnum)v.GetValue(0); + } + return defaultVal; + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/JetBrains.Annotations.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/JetBrains.Annotations.cs index 1d7ef3a80..9d799a2f8 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/JetBrains.Annotations.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/JetBrains.Annotations.cs @@ -1,1039 +1,1035 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - /// - /// Indicates that the value of the marked element could be null sometimes, - /// so the check for null is necessary before its usage. - /// - /// - /// [CanBeNull] object Test() => null; - /// - /// void UseTest() { - /// var p = Test(); - /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] - internal sealed class CanBeNullAttribute : Attribute { } - - /// - /// Indicates that the value of the marked element could never be null. - /// - /// - /// [NotNull] object Foo() { - /// return null; // Warning: Possible 'null' assignment - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] - internal sealed class NotNullAttribute : Attribute { } - - /// - /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can never be null. - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemNotNullAttribute : Attribute { } - - /// - /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can be null. - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemCanBeNullAttribute : Attribute { } - - /// - /// Indicates that the marked method builds string by format pattern and (optional) arguments. - /// Parameter, which contains format string, should be given in constructor. The format string - /// should be in -like form. - /// - /// - /// [StringFormatMethod("message")] - /// void ShowError(string message, params object[] args) { /* do something */ } - /// - /// void Foo() { - /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string - /// } - /// - [AttributeUsage( - AttributeTargets.Constructor | AttributeTargets.Method | - AttributeTargets.Property | AttributeTargets.Delegate)] - internal sealed class StringFormatMethodAttribute : Attribute - { - /// - /// Specifies which parameter of an annotated method should be treated as format-string - /// - public StringFormatMethodAttribute([NotNull] string formatParameterName) - { - FormatParameterName = formatParameterName; - } - - [NotNull] public string FormatParameterName { get; private set; } - } - - /// - /// For a parameter that is expected to be one of the limited set of values. - /// Specify fields of which type should be used as values for this parameter. - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, - AllowMultiple = true)] - internal sealed class ValueProviderAttribute : Attribute - { - public ValueProviderAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; private set; } - } - - /// - /// Indicates that the function argument should be string literal and match one - /// of the parameters of the caller function. For example, ReSharper annotates - /// the parameter of . - /// - /// - /// void Foo(string param) { - /// if (param == null) - /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InvokerParameterNameAttribute : Attribute { } - - /// - /// Indicates that the method is contained in a type that implements - /// System.ComponentModel.INotifyPropertyChanged interface and this method - /// is used to notify that some property value changed. - /// - /// - /// The method should be non-static and conform to one of the supported signatures: - /// - /// NotifyChanged(string) - /// NotifyChanged(params string[]) - /// NotifyChanged{T}(Expression{Func{T}}) - /// NotifyChanged{T,U}(Expression{Func{T,U}}) - /// SetProperty{T}(ref T, T, string) - /// - /// - /// - /// public class Foo : INotifyPropertyChanged { - /// public event PropertyChangedEventHandler PropertyChanged; - /// - /// [NotifyPropertyChangedInvocator] - /// protected virtual void NotifyChanged(string propertyName) { ... } - /// - /// string _name; - /// - /// public string Name { - /// get { return _name; } - /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } - /// } - /// } - /// - /// Examples of generated notifications: - /// - /// NotifyChanged("Property") - /// NotifyChanged(() => Property) - /// NotifyChanged((VM x) => x.Property) - /// SetProperty(ref myField, value, "Property") - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute - { - public NotifyPropertyChangedInvocatorAttribute() { } - public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) - { - ParameterName = parameterName; - } - - [CanBeNull] public string ParameterName { get; private set; } - } - - /// - /// Describes dependency between method input and output. - /// - /// - ///

Function Definition Table syntax:

- /// - /// FDT ::= FDTRow [;FDTRow]* - /// FDTRow ::= Input => Output | Output <= Input - /// Input ::= ParameterName: Value [, Input]* - /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} - /// Value ::= true | false | null | notnull | canbenull - /// - /// If method has single input parameter, it's name could be omitted.
- /// Using halt (or void/nothing, which is the same) for method output - /// means that the methos doesn't return normally (throws or terminates the process).
- /// Value canbenull is only applicable for output parameters.
- /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute - /// with rows separated by semicolon. There is no notion of order rows, all rows are checked - /// for applicability and applied per each program state tracked by R# analysis.
- ///
- /// - /// - /// [ContractAnnotation("=> halt")] - /// public void TerminationMethod() - /// - /// - /// [ContractAnnotation("halt <= condition: false")] - /// public void Assert(bool condition, string text) // regular assertion method - /// - /// - /// [ContractAnnotation("s:null => true")] - /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() - /// - /// - /// // A method that returns null if the parameter is null, - /// // and not null if the parameter is not null - /// [ContractAnnotation("null => null; notnull => notnull")] - /// public object Transform(object data) - /// - /// - /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] - /// public bool TryParse(string s, out Person result) - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - internal sealed class ContractAnnotationAttribute : Attribute - { - public ContractAnnotationAttribute([NotNull] string contract) - : this(contract, false) { } - - public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) - { - Contract = contract; - ForceFullStates = forceFullStates; - } - - [NotNull] public string Contract { get; private set; } - - public bool ForceFullStates { get; private set; } - } - - /// - /// Indicates that marked element should be localized or not. - /// - /// - /// [LocalizationRequiredAttribute(true)] - /// class Foo { - /// string str = "my string"; // Warning: Localizable string - /// } - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class LocalizationRequiredAttribute : Attribute - { - public LocalizationRequiredAttribute() : this(true) { } - - public LocalizationRequiredAttribute(bool required) - { - Required = required; - } - - public bool Required { get; private set; } - } - - /// - /// Indicates that the value of the marked type (or its derivatives) - /// cannot be compared using '==' or '!=' operators and Equals() - /// should be used instead. However, using '==' or '!=' for comparison - /// with null is always permitted. - /// - /// - /// [CannotApplyEqualityOperator] - /// class NoEquality { } - /// - /// class UsesNoEquality { - /// void Test() { - /// var ca1 = new NoEquality(); - /// var ca2 = new NoEquality(); - /// if (ca1 != null) { // OK - /// bool condition = ca1 == ca2; // Warning - /// } - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] - internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } - - /// - /// When applied to a target attribute, specifies a requirement for any type marked - /// with the target attribute to implement or inherit specific type or types. - /// - /// - /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement - /// class ComponentAttribute : Attribute { } - /// - /// [Component] // ComponentAttribute requires implementing IComponent interface - /// class MyComponent : IComponent { } - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - [BaseTypeRequired(typeof(Attribute))] - internal sealed class BaseTypeRequiredAttribute : Attribute - { - public BaseTypeRequiredAttribute([NotNull] Type baseType) - { - BaseType = baseType; - } - - [NotNull] public Type BaseType { get; private set; } - } - - /// - /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), - /// so this symbol will not be marked as unused (as well as by other usage inspections). - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class UsedImplicitlyAttribute : Attribute - { - public UsedImplicitlyAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } - - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) { } - - public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) { } - - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } - - public ImplicitUseKindFlags UseKindFlags { get; private set; } - - public ImplicitUseTargetFlags TargetFlags { get; private set; } - } - - /// - /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes - /// as unused (as well as by other usage inspections) - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] - internal sealed class MeansImplicitUseAttribute : Attribute - { - public MeansImplicitUseAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } - - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) { } - - public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) { } - - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } - - [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } - - [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } - } - - [Flags] - internal enum ImplicitUseKindFlags - { - Default = Access | Assign | InstantiatedWithFixedConstructorSignature, - /// Only entity marked with attribute considered used. - Access = 1, - /// Indicates implicit assignment to a member. - Assign = 2, - /// - /// Indicates implicit instantiation of a type with fixed constructor signature. - /// That means any unused constructor parameters won't be reported as such. - /// - InstantiatedWithFixedConstructorSignature = 4, - /// Indicates implicit instantiation of a type. - InstantiatedNoFixedConstructorSignature = 8, - } - - /// - /// Specify what is considered used implicitly when marked - /// with or . - /// - [Flags] - internal enum ImplicitUseTargetFlags - { - Default = Itself, - Itself = 1, - /// Members of entity marked with attribute are considered used. - Members = 2, - /// Entity marked with attribute and all its members considered used. - WithMembers = Itself | Members - } - - /// - /// This attribute is intended to mark publicly available API - /// which should not be removed and so is treated as used. - /// - [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] - internal sealed class PublicAPIAttribute : Attribute - { - public PublicAPIAttribute() { } - - public PublicAPIAttribute([NotNull] string comment) - { - Comment = comment; - } - - [CanBeNull] public string Comment { get; private set; } - } - - /// - /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. - /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. - /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InstantHandleAttribute : Attribute { } - - /// - /// Indicates that a method does not make any observable state changes. - /// The same as System.Diagnostics.Contracts.PureAttribute. - /// - /// - /// [Pure] int Multiply(int x, int y) => x * y; - /// - /// void M() { - /// Multiply(123, 42); // Waring: Return value of pure method is not used - /// } - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class PureAttribute : Attribute { } - - /// - /// Indicates that the return value of method invocation must be used. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class MustUseReturnValueAttribute : Attribute - { - public MustUseReturnValueAttribute() { } - - public MustUseReturnValueAttribute([NotNull] string justification) - { - Justification = justification; - } - - [CanBeNull] public string Justification { get; private set; } - } - - /// - /// Indicates the type member or parameter of some type, that should be used instead of all other ways - /// to get the value that type. This annotation is useful when you have some "context" value evaluated - /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. - /// - /// - /// class Foo { - /// [ProvidesContext] IBarService _barService = ...; - /// - /// void ProcessNode(INode node) { - /// DoSomething(node, node.GetGlobalServices().Bar); - /// // ^ Warning: use value of '_barService' field - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] - internal sealed class ProvidesContextAttribute : Attribute { } - - /// - /// Indicates that a parameter is a path to a file or a folder within a web project. - /// Path can be relative or absolute, starting from web root (~). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class PathReferenceAttribute : Attribute - { - public PathReferenceAttribute() { } - - public PathReferenceAttribute([NotNull, PathReference] string basePath) - { - BasePath = basePath; - } - - [CanBeNull] public string BasePath { get; private set; } - } - - /// - /// An extension method marked with this attribute is processed by ReSharper code completion - /// as a 'Source Template'. When extension method is completed over some expression, it's source code - /// is automatically expanded like a template at call site. - /// - /// - /// Template method body can contain valid source code and/or special comments starting with '$'. - /// Text inside these comments is added as source code when the template is applied. Template parameters - /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. - /// Use the attribute to specify macros for parameters. - /// - /// - /// In this example, the 'forEach' method is a source template available over all values - /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: - /// - /// [SourceTemplate] - /// public static void forEach<T>(this IEnumerable<T> xs) { - /// foreach (var x in xs) { - /// //$ $END$ - /// } - /// } - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class SourceTemplateAttribute : Attribute { } - - /// - /// Allows specifying a macro for a parameter of a source template. - /// - /// - /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression - /// is defined in the property. When applied on a method, the target - /// template parameter is defined in the property. To apply the macro silently - /// for the parameter, set the property value = -1. - /// - /// - /// Applying the attribute on a source template method: - /// - /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] - /// public static void forEach<T>(this IEnumerable<T> collection) { - /// foreach (var item in collection) { - /// //$ $END$ - /// } - /// } - /// - /// Applying the attribute on a template method parameter: - /// - /// [SourceTemplate] - /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { - /// /*$ var $x$Id = "$newguid$" + x.ToString(); - /// x.DoSomething($x$Id); */ - /// } - /// - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] - internal sealed class MacroAttribute : Attribute - { - /// - /// Allows specifying a macro that will be executed for a source template - /// parameter when the template is expanded. - /// - [CanBeNull] public string Expression { get; set; } - - /// - /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. - /// - /// - /// If the target parameter is used several times in the template, only one occurrence becomes editable; - /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, - /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. - /// > - public int Editable { get; set; } - - /// - /// Identifies the target parameter of a source template if the - /// is applied on a template method. - /// - [CanBeNull] public string Target { get; set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute - { - public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute - { - public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute - { - public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcMasterLocationFormatAttribute : Attribute - { - public AspMvcMasterLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute - { - public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcViewLocationFormatAttribute : Attribute - { - public AspMvcViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC action. If applied to a method, the MVC action name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcActionAttribute : Attribute - { - public AspMvcActionAttribute() { } - - public AspMvcActionAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - [CanBeNull] public string AnonymousProperty { get; private set; } - } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcAreaAttribute : Attribute - { - public AspMvcAreaAttribute() { } - - public AspMvcAreaAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - [CanBeNull] public string AnonymousProperty { get; private set; } - } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is - /// an MVC controller. If applied to a method, the MVC controller name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcControllerAttribute : Attribute - { - public AspMvcControllerAttribute() { } - - public AspMvcControllerAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - [CanBeNull] public string AnonymousProperty { get; private set; } - } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcMasterAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcModelTypeAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC - /// partial view. If applied to a method, the MVC partial view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcPartialViewAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcEditorTemplateAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. - /// Use this attribute for custom wrappers similar to - /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcTemplateAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Controller.View(Object). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcViewAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component name. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcViewComponentAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component view. If applied to a method, the MVC view component view name is default. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcViewComponentViewAttribute : Attribute { } - - /// - /// ASP.NET MVC attribute. When applied to a parameter of an attribute, - /// indicates that this parameter is an MVC action name. - /// - /// - /// [ActionName("Foo")] - /// public ActionResult Login(string returnUrl) { - /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK - /// return RedirectToAction("Bar"); // Error: Cannot resolve action - /// } - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] - internal sealed class AspMvcActionSelectorAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] - internal sealed class HtmlElementAttributesAttribute : Attribute - { - public HtmlElementAttributesAttribute() { } - - public HtmlElementAttributesAttribute([NotNull] string name) - { - Name = name; - } - - [CanBeNull] public string Name { get; private set; } - } - - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class HtmlAttributeValueAttribute : Attribute - { - public HtmlAttributeValueAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; private set; } - } - - /// - /// Razor attribute. Indicates that a parameter or a method is a Razor section. - /// Use this attribute for custom wrappers similar to - /// System.Web.WebPages.WebPageBase.RenderSection(String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class RazorSectionAttribute : Attribute { } - - /// - /// Indicates how method, constructor invocation or property access - /// over collection type affects content of the collection. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] - internal sealed class CollectionAccessAttribute : Attribute - { - public CollectionAccessAttribute(CollectionAccessType collectionAccessType) - { - CollectionAccessType = collectionAccessType; - } - - public CollectionAccessType CollectionAccessType { get; private set; } - } - - [Flags] - internal enum CollectionAccessType - { - /// Method does not use or modify content of the collection. - None = 0, - /// Method only reads content of the collection but does not modify it. - Read = 1, - /// Method can change content of the collection but does not add new elements. - ModifyExistingContent = 2, - /// Method can add new elements to the collection. - UpdatedContent = ModifyExistingContent | 4 - } - - /// - /// Indicates that the marked method is assertion method, i.e. it halts control flow if - /// one of the conditions is satisfied. To set the condition, mark one of the parameters with - /// attribute. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class AssertionMethodAttribute : Attribute { } - - /// - /// Indicates the condition parameter of the assertion method. The method itself should be - /// marked by attribute. The mandatory argument of - /// the attribute is the assertion type. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AssertionConditionAttribute : Attribute - { - public AssertionConditionAttribute(AssertionConditionType conditionType) - { - ConditionType = conditionType; - } - - public AssertionConditionType ConditionType { get; private set; } - } - - /// - /// Specifies assertion type. If the assertion method argument satisfies the condition, - /// then the execution continues. Otherwise, execution is assumed to be halted. - /// - internal enum AssertionConditionType - { - /// Marked parameter should be evaluated to true. - IS_TRUE = 0, - /// Marked parameter should be evaluated to false. - IS_FALSE = 1, - /// Marked parameter should be evaluated to null value. - IS_NULL = 2, - /// Marked parameter should be evaluated to not null value. - IS_NOT_NULL = 3, - } - - /// - /// Indicates that the marked method unconditionally terminates control flow execution. - /// For example, it could unconditionally throw exception. - /// - [Obsolete("Use [ContractAnnotation('=> halt')] instead")] - [AttributeUsage(AttributeTargets.Method)] - internal sealed class TerminatesProgramAttribute : Attribute { } - - /// - /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, - /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters - /// of delegate type by analyzing LINQ method chains. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class LinqTunnelAttribute : Attribute { } - - /// - /// Indicates that IEnumerable, passed as parameter, is not enumerated. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class NoEnumerationAttribute : Attribute { } - - /// - /// Indicates that parameter is regular expression pattern. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RegexPatternAttribute : Attribute { } - - /// - /// Prevents the Member Reordering feature from tossing members of the marked class. - /// - /// - /// The attribute must be mentioned in your member reordering patterns - /// - [AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] - internal sealed class NoReorderAttribute : Attribute { } - - /// - /// XAML attribute. Indicates the type that has ItemsSource property and should be treated - /// as ItemsControl-derived type, to enable inner items DataContext type resolve. - /// - [AttributeUsage(AttributeTargets.Class)] - internal sealed class XamlItemsControlAttribute : Attribute { } - - /// - /// XAML attribute. Indicates the property of some BindingBase-derived type, that - /// is used to bind some item of ItemsControl-derived type. This annotation will - /// enable the DataContext type resolve for XAML bindings for such properties. - /// - /// - /// Property should have the tree ancestor of the ItemsControl type or - /// marked with the attribute. - /// - [AttributeUsage(AttributeTargets.Property)] - internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal sealed class AspChildControlTypeAttribute : Attribute - { - public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) - { - TagName = tagName; - ControlType = controlType; - } - - [NotNull] public string TagName { get; private set; } - - [NotNull] public Type ControlType { get; private set; } - } - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - internal sealed class AspDataFieldAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - internal sealed class AspDataFieldsAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class AspMethodPropertyAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal sealed class AspRequiredAttributeAttribute : Attribute - { - public AspRequiredAttributeAttribute([NotNull] string attribute) - { - Attribute = attribute; - } - - [NotNull] public string Attribute { get; private set; } - } - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class AspTypePropertyAttribute : Attribute - { - public bool CreateConstructorReferences { get; private set; } - - public AspTypePropertyAttribute(bool createConstructorReferences) - { - CreateConstructorReferences = createConstructorReferences; - } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorImportNamespaceAttribute : Attribute - { - public RazorImportNamespaceAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorInjectionAttribute : Attribute - { - public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) - { - Type = type; - FieldName = fieldName; - } - - [NotNull] public string Type { get; private set; } - - [NotNull] public string FieldName { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorDirectiveAttribute : Attribute - { - public RazorDirectiveAttribute([NotNull] string directive) - { - Directive = directive; - } - - [NotNull] public string Directive { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorPageBaseTypeAttribute : Attribute - { - public RazorPageBaseTypeAttribute([NotNull] string baseType) - { - BaseType = baseType; - } - public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) - { - BaseType = baseType; - PageName = pageName; - } - - [NotNull] public string BaseType { get; private set; } - [CanBeNull] public string PageName { get; private set; } - } - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorHelperCommonAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class RazorLayoutAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorWriteMethodAttribute : Attribute { } - - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RazorWriteMethodParameterAttribute : Attribute { } + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + internal sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + internal sealed class NotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + internal sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + internal sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + internal sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + internal sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for method output + /// means that the methos doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by R# analysis.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + internal sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; private set; } + + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + internal sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + internal sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + internal sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + internal sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + internal enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + internal enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + internal sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + internal sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of method invocation must be used. + /// + [AttributeUsage(AttributeTargets.Method)] + internal sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; private set; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + internal sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; private set; } + } + + /// + /// An extension method marked with this attribute is processed by ReSharper code completion + /// as a 'Source Template'. When extension method is completed over some expression, it's source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + internal sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + internal sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// > + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + internal sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + internal sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + internal sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + internal sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + internal sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + internal sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + internal sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + internal sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + internal sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + internal sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + internal sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation or property access + /// over collection type affects content of the collection. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + internal sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + internal enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + internal sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + internal enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + internal sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + internal sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + internal sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + internal sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + internal sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; private set; } + + [NotNull] public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + internal sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + internal sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + internal sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + internal sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + internal sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + internal sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + internal sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; private set; } + + [NotNull] public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + internal sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + internal sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; private set; } + [CanBeNull] public string PageName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + internal sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + internal sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + internal sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class RazorWriteMethodParameterAttribute : Attribute { } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/NamedValueCollection.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/NamedValueCollection.cs index d518061af..a60267140 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/NamedValueCollection.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/NamedValueCollection.cs @@ -2,618 +2,615 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using System.Xml.Serialization; using winPEAS.TaskScheduler.TaskEditor.Native; using winPEAS.TaskScheduler.V2; namespace winPEAS.TaskScheduler { - /// - /// Pair of name and value. - /// - [PublicAPI] - public class NameValuePair : IXmlSerializable, INotifyPropertyChanged, ICloneable, IEquatable, IEquatable - { - private readonly ITaskNamedValuePair v2Pair; - private string name, value; - - /// - /// Occurs when a property has changed. - /// - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Initializes a new instance of the class. - /// - public NameValuePair() { } - - internal NameValuePair([NotNull] ITaskNamedValuePair iPair) - { - v2Pair = iPair; - } - - internal NameValuePair([NotNull] string name, [NotNull] string value) - { - if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) - throw new ArgumentException("Both name and value must be non-empty strings."); - this.name = name; this.value = value; - } - - [XmlIgnore] - internal bool AttributedXmlFormat { get; set; } = true; - - /// - /// Gets or sets the name. - /// - /// - /// The name. - /// - [NotNull] - public string Name - { - get { return v2Pair == null ? name : v2Pair.Name; } - set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Name)); if (v2Pair == null) name = value; else v2Pair.Name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } - } - - /// - /// Gets or sets the value. - /// - /// - /// The value. - /// - [NotNull] - public string Value - { - get { return v2Pair == null ? value : v2Pair.Value; } - set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Value)); if (v2Pair == null) this.value = value; else v2Pair.Value = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); } - } - - /// - /// Clones this instance. - /// - /// A copy of an unbound . - [NotNull] - public NameValuePair Clone() => new NameValuePair(Name, Value); - - object ICloneable.Clone() => Clone(); - - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - var valuePair = obj as ITaskNamedValuePair; - if (valuePair != null) - return (this as IEquatable).Equals(valuePair); - var pair = obj as NameValuePair; - if (pair != null) - return Equals(pair); - return base.Equals(obj); - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public bool Equals([NotNull] NameValuePair other) => other.Name == Name && other.Value == Value; - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - bool IEquatable.Equals(ITaskNamedValuePair other) => other.Name == Name && other.Value == Value; - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() => new { A = Name, B = Value }.GetHashCode(); - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() => $"{Name}={Value}"; - - /// - /// Implements the operator implicit NameValuePair. - /// - /// The KeyValuePair. - /// - /// The result of the operator. - /// - public static implicit operator NameValuePair(KeyValuePair kvp) => new NameValuePair(kvp.Key, kvp.Value); - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) - { - if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Value") - { - Name = reader.GetAttribute("name"); - Value = reader.ReadString(); - reader.Read(); - } - else - { - reader.ReadStartElement(); - XmlSerializationHelper.ReadObjectProperties(reader, this); - reader.ReadEndElement(); - } - } - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) - { - if (AttributedXmlFormat) - { - writer.WriteAttributeString("name", Name); - writer.WriteString(Value); - } - else - { - XmlSerializationHelper.WriteObjectProperties(writer, this); - } - } - } - - /// - /// Contains a collection of name-value pairs. - /// - [PublicAPI] - public sealed class NamedValueCollection : IDisposable, ICollection, IDictionary, INotifyCollectionChanged, INotifyPropertyChanged - { - private ITaskNamedValueCollection v2Coll; - private readonly List unboundDict; - - /// - /// Occurs when the collection has changed. - /// - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// - /// Occurs when a property has changed. - /// - public event PropertyChangedEventHandler PropertyChanged; - - internal NamedValueCollection([NotNull] ITaskNamedValueCollection iColl) { v2Coll = iColl; } - - internal NamedValueCollection() - { - unboundDict = new List(5); - } - - [XmlIgnore] - internal bool AttributedXmlFormat { get; set; } = true; - - internal void Bind([NotNull] ITaskNamedValueCollection iTaskNamedValueCollection) - { - v2Coll = iTaskNamedValueCollection; - v2Coll.Clear(); - foreach (var item in unboundDict) - v2Coll.Create(item.Name, item.Value); - } - - /// - /// Copies current to another. - /// - /// The destination collection. - public void CopyTo([NotNull] NamedValueCollection destCollection) - { - if (v2Coll != null) - { - for (var i = 1; i <= Count; i++) - destCollection.Add(v2Coll[i].Name, v2Coll[i].Value); - } - else - { - foreach (var item in unboundDict) - destCollection.Add(item.Name, item.Value); - } - } - - /// - /// Releases all resources used by this class. - /// - public void Dispose() - { - if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); - } - - /// - /// Gets the number of items in the collection. - /// - public int Count => v2Coll?.Count ?? unboundDict.Count; - - /// - /// Gets a collection of the names. - /// - /// - /// The names. - /// - [ItemNotNull, NotNull] - public ICollection Names - { - get - { - if (v2Coll == null) - return unboundDict.ConvertAll(p => p.Name); - - var ret = new List(v2Coll.Count); - foreach (var item in this) - ret.Add(item.Name); - return ret; - } - } - - /// - /// Gets a collection of the values. - /// - /// - /// The values. - /// - [ItemNotNull, NotNull] - public ICollection Values - { - get - { - if (v2Coll == null) - return unboundDict.ConvertAll(p => p.Value); - - var ret = new List(v2Coll.Count); - foreach (var item in this) - ret.Add(item.Value); - return ret; - } - } - - /// - /// Gets the value of the item at the specified index. - /// - /// The index of the item being requested. - /// The value of the name-value pair at the specified index. - [NotNull] - public string this[int index] - { - get - { - if (v2Coll != null) - return v2Coll[++index].Value; - return unboundDict[index].Value; - } - } - - /// - /// Gets the value of the item with the specified name. - /// - /// Name to get the value for. - /// Value for the name, or null if not found. - public string this[string name] - { - [CanBeNull] - get - { - string ret; - TryGetValue(name, out ret); - return ret; - } - [NotNull] - set - { - int idx; - NameValuePair old = null; - var nvp = new NameValuePair(name, value); - if (v2Coll == null) - { - idx = unboundDict.FindIndex(p => p.Name == name); - if (idx == -1) - unboundDict.Add(nvp); - else - { - old = unboundDict[idx]; - unboundDict[idx] = nvp; - } - } - else - { - var array = new KeyValuePair[Count]; - ((ICollection>)this).CopyTo(array, 0); - idx = Array.FindIndex(array, p => p.Key == name); - if (idx == -1) - v2Coll.Create(name, value); - else - { - old = array[idx]; - array[idx] = new KeyValuePair(name, value); - v2Coll.Clear(); - foreach (KeyValuePair t in array) - v2Coll.Create(t.Key, t.Value); - } - } - if (idx == -1) - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, nvp)); - else - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, nvp, old, idx)); - } - } - - /// - /// Adds an item to the . - /// - /// The object to add to the . - public void Add([NotNull] NameValuePair item) - { - if (v2Coll != null) - v2Coll.Create(item.Name, item.Value); - else - unboundDict.Add(item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - } - - /// - /// Adds a name-value pair to the collection. - /// - /// The name associated with a value in a name-value pair. - /// The value associated with a name in a name-value pair. - public void Add([NotNull] string name, [NotNull] string value) - { - Add(new NameValuePair(name, value)); - } - - /// - /// Adds the elements of the specified collection to the end of . - /// - /// The collection of whose elements should be added to the end of . - public void AddRange([ItemNotNull, NotNull] IEnumerable items) - { - if (v2Coll != null) - { - foreach (var item in items) - v2Coll.Create(item.Name, item.Value); - } - else - unboundDict.AddRange(items); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items)); - } - - /// - /// Clears the entire collection of name-value pairs. - /// - public void Clear() - { - if (v2Coll != null) - v2Coll.Clear(); - else - unboundDict.Clear(); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - if (v2Coll == null) - return unboundDict.GetEnumerator(); - - return new ComEnumerator(() => v2Coll.Count, i => v2Coll[i], o => new NameValuePair(o)); - } - - private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - if (e.NewItems != null) - foreach (NameValuePair item in e.NewItems) - item.AttributedXmlFormat = AttributedXmlFormat; - CollectionChanged?.Invoke(this, e); - } - - /// - /// Removes the name-value pair with the specified key from the collection. - /// - /// The name associated with a value in a name-value pair. - /// true if item successfully removed; false otherwise. - public bool Remove([NotNull] string name) - { - var i = -1; - NameValuePair nvp = null; - try - { - if (v2Coll == null) - { - i = unboundDict.FindIndex(p => p.Name == name); - if (i != -1) - { - nvp = unboundDict[i]; - unboundDict.RemoveAt(i); - } - return (i != -1); - } - - for (i = 0; i < v2Coll.Count; i++) - { - if (name == v2Coll[i].Name) - { - nvp = new NameValuePair(v2Coll[i]).Clone(); - v2Coll.Remove(i); - return true; - } - } - i = -1; - } - finally - { - if (i != -1) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, nvp, i)); - } - } - return false; - } - - /// - /// Removes a selected name-value pair from the collection. - /// - /// Index of the pair to remove. - public void RemoveAt(int index) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index)); - NameValuePair nvp; - if (v2Coll != null) - { - nvp = new NameValuePair(v2Coll[index]).Clone(); - v2Coll.Remove(index); - } - else - { - nvp = unboundDict[index]; - unboundDict.RemoveAt(index); - } - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, nvp, index)); - } - - /// - /// Gets the value associated with the specified name. - /// - /// The name whose value to get. - /// When this method returns, the value associated with the specified name, if the name is found; otherwise, null. This parameter is passed uninitialized. - /// true if the collection contains an element with the specified name; otherwise, false. - public bool TryGetValue(string name, out string value) - { - if (v2Coll != null) - { - foreach (var item in this) - { - if (string.CompareOrdinal(item.Name, name) == 0) - { - value = item.Value; - return true; - } - } - value = null; - return false; - } - - var nvp = unboundDict.Find(p => p.Name == name); - value = nvp?.Value; - return nvp != null; - } - - /// - /// Gets the collection enumerator for the name-value collection. - /// - /// An for the collection. - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - - bool ICollection.Contains(NameValuePair item) - { - if (v2Coll == null) - return unboundDict.Contains(item); - - foreach (var invp in this) - if (Equals(item, invp)) return true; - return false; - } - - void ICollection.CopyTo(NameValuePair[] array, int arrayIndex) - { - if (v2Coll == null) - unboundDict.CopyTo(array, arrayIndex); - else - { - if (array.Length - arrayIndex < v2Coll.Count) - throw new ArgumentException("Items in collection exceed available items in destination array."); - if (arrayIndex < 0) - throw new ArgumentException(@"Array index must be 0 or greater.", nameof(arrayIndex)); - for (var i = 0; i < v2Coll.Count; i++) - array[i + arrayIndex] = new NameValuePair(v2Coll[i]); - } - } - - bool ICollection.IsReadOnly => false; - - ICollection IDictionary.Keys => Names; - - bool ICollection>.IsReadOnly => false; - - bool ICollection.Remove(NameValuePair item) - { - var i = -1; - try - { - if (v2Coll == null) - { - if ((i = unboundDict.IndexOf(item)) != -1) - return unboundDict.Remove(item); - } - else - { - for (i = 0; i < v2Coll.Count; i++) - { - if (item.Equals(v2Coll[i])) - { - v2Coll.Remove(i); - return true; - } - } - } - i = -1; - } - finally - { - if (i != -1) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, i)); - } - } - return false; - } - - bool IDictionary.ContainsKey(string key) => Names.Contains(key); - - void ICollection>.Add(KeyValuePair item) - { - Add(item.Key, item.Value); - } - - bool ICollection>.Contains(KeyValuePair item) => - ((ICollection)this).Contains(new NameValuePair(item.Key, item.Value)); - - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array.Length < Count + arrayIndex) - throw new ArgumentOutOfRangeException(nameof(array), @"Array has insufficient capacity to support copy."); - foreach (var item in ((IEnumerable>)this)) - array[arrayIndex++] = item; - } - - bool ICollection>.Remove(KeyValuePair item) => - ((ICollection)this).Remove(new NameValuePair(item.Key, item.Value)); - - IEnumerator> IEnumerable>.GetEnumerator() - { - foreach (var nvp in this) - yield return new KeyValuePair(nvp.Name, nvp.Value); - } - } + /// + /// Pair of name and value. + /// + [PublicAPI] + public class NameValuePair : IXmlSerializable, INotifyPropertyChanged, ICloneable, IEquatable, IEquatable + { + private readonly ITaskNamedValuePair v2Pair; + private string name, value; + + /// + /// Occurs when a property has changed. + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Initializes a new instance of the class. + /// + public NameValuePair() { } + + internal NameValuePair([NotNull] ITaskNamedValuePair iPair) + { + v2Pair = iPair; + } + + internal NameValuePair([NotNull] string name, [NotNull] string value) + { + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) + throw new ArgumentException("Both name and value must be non-empty strings."); + this.name = name; this.value = value; + } + + [XmlIgnore] + internal bool AttributedXmlFormat { get; set; } = true; + + /// + /// Gets or sets the name. + /// + /// + /// The name. + /// + [NotNull] + public string Name + { + get { return v2Pair == null ? name : v2Pair.Name; } + set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Name)); if (v2Pair == null) name = value; else v2Pair.Name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } + } + + /// + /// Gets or sets the value. + /// + /// + /// The value. + /// + [NotNull] + public string Value + { + get { return v2Pair == null ? value : v2Pair.Value; } + set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Value)); if (v2Pair == null) this.value = value; else v2Pair.Value = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); } + } + + /// + /// Clones this instance. + /// + /// A copy of an unbound . + [NotNull] + public NameValuePair Clone() => new NameValuePair(Name, Value); + + object ICloneable.Clone() => Clone(); + + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + var valuePair = obj as ITaskNamedValuePair; + if (valuePair != null) + return (this as IEquatable).Equals(valuePair); + var pair = obj as NameValuePair; + if (pair != null) + return Equals(pair); + return base.Equals(obj); + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public bool Equals([NotNull] NameValuePair other) => other.Name == Name && other.Value == Value; + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + bool IEquatable.Equals(ITaskNamedValuePair other) => other.Name == Name && other.Value == Value; + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() => new { A = Name, B = Value }.GetHashCode(); + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() => $"{Name}={Value}"; + + /// + /// Implements the operator implicit NameValuePair. + /// + /// The KeyValuePair. + /// + /// The result of the operator. + /// + public static implicit operator NameValuePair(KeyValuePair kvp) => new NameValuePair(kvp.Key, kvp.Value); + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) + { + if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Value") + { + Name = reader.GetAttribute("name"); + Value = reader.ReadString(); + reader.Read(); + } + else + { + reader.ReadStartElement(); + XmlSerializationHelper.ReadObjectProperties(reader, this); + reader.ReadEndElement(); + } + } + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) + { + if (AttributedXmlFormat) + { + writer.WriteAttributeString("name", Name); + writer.WriteString(Value); + } + else + { + XmlSerializationHelper.WriteObjectProperties(writer, this); + } + } + } + + /// + /// Contains a collection of name-value pairs. + /// + [PublicAPI] + public sealed class NamedValueCollection : IDisposable, ICollection, IDictionary, INotifyCollectionChanged, INotifyPropertyChanged + { + private ITaskNamedValueCollection v2Coll; + private readonly List unboundDict; + + /// + /// Occurs when the collection has changed. + /// + public event NotifyCollectionChangedEventHandler CollectionChanged; + + /// + /// Occurs when a property has changed. + /// + public event PropertyChangedEventHandler PropertyChanged; + + internal NamedValueCollection([NotNull] ITaskNamedValueCollection iColl) { v2Coll = iColl; } + + internal NamedValueCollection() + { + unboundDict = new List(5); + } + + [XmlIgnore] + internal bool AttributedXmlFormat { get; set; } = true; + + internal void Bind([NotNull] ITaskNamedValueCollection iTaskNamedValueCollection) + { + v2Coll = iTaskNamedValueCollection; + v2Coll.Clear(); + foreach (var item in unboundDict) + v2Coll.Create(item.Name, item.Value); + } + + /// + /// Copies current to another. + /// + /// The destination collection. + public void CopyTo([NotNull] NamedValueCollection destCollection) + { + if (v2Coll != null) + { + for (var i = 1; i <= Count; i++) + destCollection.Add(v2Coll[i].Name, v2Coll[i].Value); + } + else + { + foreach (var item in unboundDict) + destCollection.Add(item.Name, item.Value); + } + } + + /// + /// Releases all resources used by this class. + /// + public void Dispose() + { + if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); + } + + /// + /// Gets the number of items in the collection. + /// + public int Count => v2Coll?.Count ?? unboundDict.Count; + + /// + /// Gets a collection of the names. + /// + /// + /// The names. + /// + [ItemNotNull, NotNull] + public ICollection Names + { + get + { + if (v2Coll == null) + return unboundDict.ConvertAll(p => p.Name); + + var ret = new List(v2Coll.Count); + foreach (var item in this) + ret.Add(item.Name); + return ret; + } + } + + /// + /// Gets a collection of the values. + /// + /// + /// The values. + /// + [ItemNotNull, NotNull] + public ICollection Values + { + get + { + if (v2Coll == null) + return unboundDict.ConvertAll(p => p.Value); + + var ret = new List(v2Coll.Count); + foreach (var item in this) + ret.Add(item.Value); + return ret; + } + } + + /// + /// Gets the value of the item at the specified index. + /// + /// The index of the item being requested. + /// The value of the name-value pair at the specified index. + [NotNull] + public string this[int index] + { + get + { + if (v2Coll != null) + return v2Coll[++index].Value; + return unboundDict[index].Value; + } + } + + /// + /// Gets the value of the item with the specified name. + /// + /// Name to get the value for. + /// Value for the name, or null if not found. + public string this[string name] + { + [CanBeNull] + get + { + string ret; + TryGetValue(name, out ret); + return ret; + } + [NotNull] + set + { + int idx; + NameValuePair old = null; + var nvp = new NameValuePair(name, value); + if (v2Coll == null) + { + idx = unboundDict.FindIndex(p => p.Name == name); + if (idx == -1) + unboundDict.Add(nvp); + else + { + old = unboundDict[idx]; + unboundDict[idx] = nvp; + } + } + else + { + var array = new KeyValuePair[Count]; + ((ICollection>)this).CopyTo(array, 0); + idx = Array.FindIndex(array, p => p.Key == name); + if (idx == -1) + v2Coll.Create(name, value); + else + { + old = array[idx]; + array[idx] = new KeyValuePair(name, value); + v2Coll.Clear(); + foreach (KeyValuePair t in array) + v2Coll.Create(t.Key, t.Value); + } + } + if (idx == -1) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, nvp)); + else + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, nvp, old, idx)); + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + public void Add([NotNull] NameValuePair item) + { + if (v2Coll != null) + v2Coll.Create(item.Name, item.Value); + else + unboundDict.Add(item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); + } + + /// + /// Adds a name-value pair to the collection. + /// + /// The name associated with a value in a name-value pair. + /// The value associated with a name in a name-value pair. + public void Add([NotNull] string name, [NotNull] string value) + { + Add(new NameValuePair(name, value)); + } + + /// + /// Adds the elements of the specified collection to the end of . + /// + /// The collection of whose elements should be added to the end of . + public void AddRange([ItemNotNull, NotNull] IEnumerable items) + { + if (v2Coll != null) + { + foreach (var item in items) + v2Coll.Create(item.Name, item.Value); + } + else + unboundDict.AddRange(items); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items)); + } + + /// + /// Clears the entire collection of name-value pairs. + /// + public void Clear() + { + if (v2Coll != null) + v2Coll.Clear(); + else + unboundDict.Clear(); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + if (v2Coll == null) + return unboundDict.GetEnumerator(); + + return new ComEnumerator(() => v2Coll.Count, i => v2Coll[i], o => new NameValuePair(o)); + } + + private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if (e.NewItems != null) + foreach (NameValuePair item in e.NewItems) + item.AttributedXmlFormat = AttributedXmlFormat; + CollectionChanged?.Invoke(this, e); + } + + /// + /// Removes the name-value pair with the specified key from the collection. + /// + /// The name associated with a value in a name-value pair. + /// true if item successfully removed; false otherwise. + public bool Remove([NotNull] string name) + { + var i = -1; + NameValuePair nvp = null; + try + { + if (v2Coll == null) + { + i = unboundDict.FindIndex(p => p.Name == name); + if (i != -1) + { + nvp = unboundDict[i]; + unboundDict.RemoveAt(i); + } + return (i != -1); + } + + for (i = 0; i < v2Coll.Count; i++) + { + if (name == v2Coll[i].Name) + { + nvp = new NameValuePair(v2Coll[i]).Clone(); + v2Coll.Remove(i); + return true; + } + } + i = -1; + } + finally + { + if (i != -1) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, nvp, i)); + } + } + return false; + } + + /// + /// Removes a selected name-value pair from the collection. + /// + /// Index of the pair to remove. + public void RemoveAt(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException(nameof(index)); + NameValuePair nvp; + if (v2Coll != null) + { + nvp = new NameValuePair(v2Coll[index]).Clone(); + v2Coll.Remove(index); + } + else + { + nvp = unboundDict[index]; + unboundDict.RemoveAt(index); + } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, nvp, index)); + } + + /// + /// Gets the value associated with the specified name. + /// + /// The name whose value to get. + /// When this method returns, the value associated with the specified name, if the name is found; otherwise, null. This parameter is passed uninitialized. + /// true if the collection contains an element with the specified name; otherwise, false. + public bool TryGetValue(string name, out string value) + { + if (v2Coll != null) + { + foreach (var item in this) + { + if (string.CompareOrdinal(item.Name, name) == 0) + { + value = item.Value; + return true; + } + } + value = null; + return false; + } + + var nvp = unboundDict.Find(p => p.Name == name); + value = nvp?.Value; + return nvp != null; + } + + /// + /// Gets the collection enumerator for the name-value collection. + /// + /// An for the collection. + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + + bool ICollection.Contains(NameValuePair item) + { + if (v2Coll == null) + return unboundDict.Contains(item); + + foreach (var invp in this) + if (Equals(item, invp)) return true; + return false; + } + + void ICollection.CopyTo(NameValuePair[] array, int arrayIndex) + { + if (v2Coll == null) + unboundDict.CopyTo(array, arrayIndex); + else + { + if (array.Length - arrayIndex < v2Coll.Count) + throw new ArgumentException("Items in collection exceed available items in destination array."); + if (arrayIndex < 0) + throw new ArgumentException(@"Array index must be 0 or greater.", nameof(arrayIndex)); + for (var i = 0; i < v2Coll.Count; i++) + array[i + arrayIndex] = new NameValuePair(v2Coll[i]); + } + } + + bool ICollection.IsReadOnly => false; + + ICollection IDictionary.Keys => Names; + + bool ICollection>.IsReadOnly => false; + + bool ICollection.Remove(NameValuePair item) + { + var i = -1; + try + { + if (v2Coll == null) + { + if ((i = unboundDict.IndexOf(item)) != -1) + return unboundDict.Remove(item); + } + else + { + for (i = 0; i < v2Coll.Count; i++) + { + if (item.Equals(v2Coll[i])) + { + v2Coll.Remove(i); + return true; + } + } + } + i = -1; + } + finally + { + if (i != -1) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, i)); + } + } + return false; + } + + bool IDictionary.ContainsKey(string key) => Names.Contains(key); + + void ICollection>.Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + bool ICollection>.Contains(KeyValuePair item) => + ((ICollection)this).Contains(new NameValuePair(item.Key, item.Value)); + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array.Length < Count + arrayIndex) + throw new ArgumentOutOfRangeException(nameof(array), @"Array has insufficient capacity to support copy."); + foreach (var item in ((IEnumerable>)this)) + array[arrayIndex++] = item; + } + + bool ICollection>.Remove(KeyValuePair item) => + ((ICollection)this).Remove(new NameValuePair(item.Key, item.Value)); + + IEnumerator> IEnumerable>.GetEnumerator() + { + foreach (var nvp in this) + yield return new KeyValuePair(nvp.Name, nvp.Value); + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/NotV1SupportedException.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/NotV1SupportedException.cs index 082153142..b41d3db08 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/NotV1SupportedException.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/NotV1SupportedException.cs @@ -1,131 +1,127 @@ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - /// - /// Abstract class for throwing a method specific exception. - /// - [DebuggerStepThrough, Serializable] - [PublicAPI] - public abstract class TSNotSupportedException : Exception - { - /// Defines the minimum supported version for the action not allowed by this exception. - protected readonly TaskCompatibility min; - private readonly string myMessage; + /// + /// Abstract class for throwing a method specific exception. + /// + [DebuggerStepThrough, Serializable] + [PublicAPI] + public abstract class TSNotSupportedException : Exception + { + /// Defines the minimum supported version for the action not allowed by this exception. + protected readonly TaskCompatibility min; + private readonly string myMessage; - /// - /// Initializes a new instance of the class. - /// - /// The serialization information. - /// The streaming context. - protected TSNotSupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) - : base(serializationInfo, streamingContext) - { - try { min = (TaskCompatibility)serializationInfo.GetValue("min", typeof(TaskCompatibility)); } - catch { min = TaskCompatibility.V1; } - } + /// + /// Initializes a new instance of the class. + /// + /// The serialization information. + /// The streaming context. + protected TSNotSupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + try { min = (TaskCompatibility)serializationInfo.GetValue("min", typeof(TaskCompatibility)); } + catch { min = TaskCompatibility.V1; } + } - internal TSNotSupportedException(TaskCompatibility minComp) - { - min = minComp; - var stackTrace = new StackTrace(); - var stackFrame = stackTrace.GetFrame(2); - var methodBase = stackFrame.GetMethod(); - myMessage = $"{methodBase.DeclaringType?.Name}.{methodBase.Name} is not supported on {LibName}"; - } + internal TSNotSupportedException(TaskCompatibility minComp) + { + min = minComp; + var stackTrace = new StackTrace(); + var stackFrame = stackTrace.GetFrame(2); + var methodBase = stackFrame.GetMethod(); + myMessage = $"{methodBase.DeclaringType?.Name}.{methodBase.Name} is not supported on {LibName}"; + } - internal TSNotSupportedException(string message, TaskCompatibility minComp) - { - myMessage = message; - min = minComp; - } + internal TSNotSupportedException(string message, TaskCompatibility minComp) + { + myMessage = message; + min = minComp; + } - /// - /// Gets a message that describes the current exception. - /// - public override string Message => myMessage; + /// + /// Gets a message that describes the current exception. + /// + public override string Message => myMessage; - /// - /// Gets the minimum supported TaskScheduler version required for this method or property. - /// - public TaskCompatibility MinimumSupportedVersion => min; + /// + /// Gets the minimum supported TaskScheduler version required for this method or property. + /// + public TaskCompatibility MinimumSupportedVersion => min; - internal abstract string LibName { get; } + internal abstract string LibName { get; } - /// - /// Gets the object data. - /// - /// The information. - /// The context. - [SecurityCritical, SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - info.AddValue("min", min); - base.GetObjectData(info, context); - } - } + /// + /// Gets the object data. + /// + /// The information. + /// The context. + [SecurityCritical, SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + throw new ArgumentNullException(nameof(info)); + info.AddValue("min", min); + base.GetObjectData(info, context); + } + } - /// - /// Thrown when the calling method is not supported by Task Scheduler 1.0. - /// - [DebuggerStepThrough, Serializable] - public class NotV1SupportedException : TSNotSupportedException - { - /// - /// Initializes a new instance of the class. - /// - /// The serialization information. - /// The streaming context. - protected NotV1SupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } - internal NotV1SupportedException() : base(TaskCompatibility.V2) { } - /// - /// Initializes a new instance of the class. - /// - /// The message. - public NotV1SupportedException(string message) : base(message, TaskCompatibility.V2) { } - internal override string LibName => "Task Scheduler 1.0"; - } + /// + /// Thrown when the calling method is not supported by Task Scheduler 1.0. + /// + [DebuggerStepThrough, Serializable] + public class NotV1SupportedException : TSNotSupportedException + { + /// + /// Initializes a new instance of the class. + /// + /// The serialization information. + /// The streaming context. + protected NotV1SupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } + internal NotV1SupportedException() : base(TaskCompatibility.V2) { } + /// + /// Initializes a new instance of the class. + /// + /// The message. + public NotV1SupportedException(string message) : base(message, TaskCompatibility.V2) { } + internal override string LibName => "Task Scheduler 1.0"; + } - /// - /// Thrown when the calling method is not supported by Task Scheduler 2.0. - /// - [DebuggerStepThrough, Serializable] - public class NotV2SupportedException : TSNotSupportedException - { - /// - /// Initializes a new instance of the class. - /// - /// The serialization information. - /// The streaming context. - protected NotV2SupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } - internal NotV2SupportedException() : base(TaskCompatibility.V1) { } - internal NotV2SupportedException(string message) : base(message, TaskCompatibility.V1) { } - internal override string LibName => "Task Scheduler 2.0 (1.2)"; - } + /// + /// Thrown when the calling method is not supported by Task Scheduler 2.0. + /// + [DebuggerStepThrough, Serializable] + public class NotV2SupportedException : TSNotSupportedException + { + /// + /// Initializes a new instance of the class. + /// + /// The serialization information. + /// The streaming context. + protected NotV2SupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } + internal NotV2SupportedException() : base(TaskCompatibility.V1) { } + internal NotV2SupportedException(string message) : base(message, TaskCompatibility.V1) { } + internal override string LibName => "Task Scheduler 2.0 (1.2)"; + } - /// - /// Thrown when the calling method is not supported by Task Scheduler versions prior to the one specified. - /// - [DebuggerStepThrough, Serializable] - public class NotSupportedPriorToException : TSNotSupportedException - { - /// - /// Initializes a new instance of the class. - /// - /// The serialization information. - /// The streaming context. - protected NotSupportedPriorToException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } - internal NotSupportedPriorToException(TaskCompatibility supportedVersion) : base(supportedVersion) { } - internal override string LibName => $"Task Scheduler versions prior to 2.{((int)min) - 2} (1.{(int)min})"; - } + /// + /// Thrown when the calling method is not supported by Task Scheduler versions prior to the one specified. + /// + [DebuggerStepThrough, Serializable] + public class NotSupportedPriorToException : TSNotSupportedException + { + /// + /// Initializes a new instance of the class. + /// + /// The serialization information. + /// The streaming context. + protected NotSupportedPriorToException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } + internal NotSupportedPriorToException(TaskCompatibility supportedVersion) : base(supportedVersion) { } + internal override string LibName => $"Task Scheduler versions prior to 2.{((int)min) - 2} (1.{(int)min})"; + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/ReflectionHelper.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/ReflectionHelper.cs index 065ca8e2f..cb2be291e 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/ReflectionHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/ReflectionHelper.cs @@ -1,139 +1,135 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - /// Extensions related to System.Reflection - internal static class ReflectionHelper - { - /// Loads a type from a named assembly. - /// Name of the type. - /// The name or path of the file that contains the manifest of the assembly. - /// The reference, or null if type or assembly not found. - public static Type LoadType(string typeName, string asmRef) - { - Type ret = null; - if (!TryGetType(Assembly.GetCallingAssembly(), typeName, ref ret)) - if (!TryGetType(asmRef, typeName, ref ret)) - if (!TryGetType(Assembly.GetExecutingAssembly(), typeName, ref ret)) - TryGetType(Assembly.GetEntryAssembly(), typeName, ref ret); - return ret; - } + /// Extensions related to System.Reflection + internal static class ReflectionHelper + { + /// Loads a type from a named assembly. + /// Name of the type. + /// The name or path of the file that contains the manifest of the assembly. + /// The reference, or null if type or assembly not found. + public static Type LoadType(string typeName, string asmRef) + { + Type ret = null; + if (!TryGetType(Assembly.GetCallingAssembly(), typeName, ref ret)) + if (!TryGetType(asmRef, typeName, ref ret)) + if (!TryGetType(Assembly.GetExecutingAssembly(), typeName, ref ret)) + TryGetType(Assembly.GetEntryAssembly(), typeName, ref ret); + return ret; + } - /// Tries the retrieve a reference from an assembly. - /// Name of the type. - /// The assembly reference name from which to load the type. - /// The reference, if found. - /// true if the type was found in the assembly; otherwise, false. - private static bool TryGetType(string asmRef, string typeName, ref Type type) - { - try - { - if (System.IO.File.Exists(asmRef)) - return TryGetType(Assembly.LoadFrom(asmRef), typeName, ref type); - } - catch { } - return false; - } + /// Tries the retrieve a reference from an assembly. + /// Name of the type. + /// The assembly reference name from which to load the type. + /// The reference, if found. + /// true if the type was found in the assembly; otherwise, false. + private static bool TryGetType(string asmRef, string typeName, ref Type type) + { + try + { + if (System.IO.File.Exists(asmRef)) + return TryGetType(Assembly.LoadFrom(asmRef), typeName, ref type); + } + catch { } + return false; + } - /// Tries the retrieve a reference from an assembly. - /// Name of the type. - /// The assembly from which to load the type. - /// The reference, if found. - /// true if the type was found in the assembly; otherwise, false. - private static bool TryGetType(Assembly asm, string typeName, ref Type type) - { - if (asm != null) - { - try - { - type = asm.GetType(typeName, false, false); - return (type != null); - } - catch { } - } - return false; - } + /// Tries the retrieve a reference from an assembly. + /// Name of the type. + /// The assembly from which to load the type. + /// The reference, if found. + /// true if the type was found in the assembly; otherwise, false. + private static bool TryGetType(Assembly asm, string typeName, ref Type type) + { + if (asm != null) + { + try + { + type = asm.GetType(typeName, false, false); + return (type != null); + } + catch { } + } + return false; + } - /// Invokes a named method on a created instance of a type with parameters. - /// The expected type of the method's return value. - /// The type to be instantiated and then used to invoke the method. This method assumes the type has a default public constructor. - /// Name of the method. - /// The arguments to provide to the method invocation. - /// The value returned from the method. - public static T InvokeMethod(Type type, string methodName, params object[] args) - { - object o = Activator.CreateInstance(type); - return InvokeMethod(o, methodName, args); - } + /// Invokes a named method on a created instance of a type with parameters. + /// The expected type of the method's return value. + /// The type to be instantiated and then used to invoke the method. This method assumes the type has a default public constructor. + /// Name of the method. + /// The arguments to provide to the method invocation. + /// The value returned from the method. + public static T InvokeMethod(Type type, string methodName, params object[] args) + { + object o = Activator.CreateInstance(type); + return InvokeMethod(o, methodName, args); + } - /// Invokes a named method on a created instance of a type with parameters. - /// The expected type of the method's return value. - /// The type to be instantiated and then used to invoke the method. - /// The arguments to supply to the constructor. - /// Name of the method. - /// The arguments to provide to the method invocation. - /// The value returned from the method. - public static T InvokeMethod(Type type, object[] instArgs, string methodName, params object[] args) - { - object o = Activator.CreateInstance(type, instArgs); - return InvokeMethod(o, methodName, args); - } + /// Invokes a named method on a created instance of a type with parameters. + /// The expected type of the method's return value. + /// The type to be instantiated and then used to invoke the method. + /// The arguments to supply to the constructor. + /// Name of the method. + /// The arguments to provide to the method invocation. + /// The value returned from the method. + public static T InvokeMethod(Type type, object[] instArgs, string methodName, params object[] args) + { + object o = Activator.CreateInstance(type, instArgs); + return InvokeMethod(o, methodName, args); + } - /// Invokes a named method on an object with parameters and no return value. - /// The object on which to invoke the method. - /// Name of the method. - /// The arguments to provide to the method invocation. - public static T InvokeMethod(object obj, string methodName, params object[] args) - { - Type[] argTypes = (args == null || args.Length == 0) ? Type.EmptyTypes : Array.ConvertAll(args, delegate (object o) { return o == null ? typeof(object) : o.GetType(); }); - return InvokeMethod(obj, methodName, argTypes, args); - } + /// Invokes a named method on an object with parameters and no return value. + /// The object on which to invoke the method. + /// Name of the method. + /// The arguments to provide to the method invocation. + public static T InvokeMethod(object obj, string methodName, params object[] args) + { + Type[] argTypes = (args == null || args.Length == 0) ? Type.EmptyTypes : Array.ConvertAll(args, delegate (object o) { return o == null ? typeof(object) : o.GetType(); }); + return InvokeMethod(obj, methodName, argTypes, args); + } - /// Invokes a named method on an object with parameters and no return value. - /// The expected type of the method's return value. - /// The object on which to invoke the method. - /// Name of the method. - /// The types of the . - /// The arguments to provide to the method invocation. - /// The value returned from the method. - public static T InvokeMethod(object obj, string methodName, Type[] argTypes, object[] args) - { - MethodInfo mi = obj?.GetType().GetMethod(methodName, argTypes); - if (mi != null) - return (T)Convert.ChangeType(mi.Invoke(obj, args), typeof(T)); - return default(T); - } + /// Invokes a named method on an object with parameters and no return value. + /// The expected type of the method's return value. + /// The object on which to invoke the method. + /// Name of the method. + /// The types of the . + /// The arguments to provide to the method invocation. + /// The value returned from the method. + public static T InvokeMethod(object obj, string methodName, Type[] argTypes, object[] args) + { + MethodInfo mi = obj?.GetType().GetMethod(methodName, argTypes); + if (mi != null) + return (T)Convert.ChangeType(mi.Invoke(obj, args), typeof(T)); + return default(T); + } - /// Gets a named property value from an object. - /// The expected type of the property to be returned. - /// The object from which to retrieve the property. - /// Name of the property. - /// The default value to return in the instance that the property is not found. - /// The property value, if found, or the if not. - public static T GetProperty(object obj, string propName, T defaultValue = default(T)) - { - if (obj != null) - { - try { return (T)Convert.ChangeType(obj.GetType().InvokeMember(propName, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, obj, null, null), typeof(T)); } - catch { } - } - return defaultValue; - } + /// Gets a named property value from an object. + /// The expected type of the property to be returned. + /// The object from which to retrieve the property. + /// Name of the property. + /// The default value to return in the instance that the property is not found. + /// The property value, if found, or the if not. + public static T GetProperty(object obj, string propName, T defaultValue = default(T)) + { + if (obj != null) + { + try { return (T)Convert.ChangeType(obj.GetType().InvokeMember(propName, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, obj, null, null), typeof(T)); } + catch { } + } + return defaultValue; + } - /// Sets a named property on an object. - /// The type of the property to be set. - /// The object on which to set the property. - /// Name of the property. - /// The property value to set on the object. - public static void SetProperty(object obj, string propName, T value) - { - try { obj?.GetType().InvokeMember(propName, BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, obj, new object[] { value }, null); } - catch { } - } - } + /// Sets a named property on an object. + /// The type of the property to be set. + /// The object on which to set the property. + /// Name of the property. + /// The property value to set on the object. + public static void SetProperty(object obj, string propName, T value) + { + try { obj?.GetType().InvokeMember(propName, BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, obj, new object[] { value }, null); } + catch { } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Task.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Task.cs index c69b47e47..1696d33fe 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Task.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Task.cs @@ -29,501 +29,501 @@ namespace winPEAS.TaskScheduler { /// Defines what versions of Task Scheduler or the AT command that the task is compatible with. public enum TaskCompatibility - { - /// The task is compatible with the AT command. - AT, - - /// - /// The task is compatible with Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). - /// Items not available when compared to V2: - /// - /// - /// TaskDefinition.Principal.GroupId - All account information can be retrieved via the UserId property. - /// - /// - /// TaskLogonType values Group, None and S4U are not supported. - /// - /// - /// TaskDefinition.Principal.RunLevel == TaskRunLevel.Highest is not supported. - /// - /// - /// - /// Assigning access security to a task is not supported using TaskDefinition.RegistrationInfo.SecurityDescriptorSddlForm or in RegisterTaskDefinition. - /// - /// - /// - /// - /// TaskDefinition.RegistrationInfo.Documentation, Source, URI and Version properties are only supported using this library. See - /// details in the remarks for . - /// - /// - /// - /// TaskDefinition.Settings.AllowDemandStart cannot be false. - /// - /// - /// TaskDefinition.Settings.AllowHardTerminate cannot be false. - /// - /// - /// TaskDefinition.Settings.MultipleInstances can only be IgnoreNew. - /// - /// - /// TaskDefinition.Settings.NetworkSettings cannot have any values. - /// - /// - /// TaskDefinition.Settings.RestartCount can only be 0. - /// - /// - /// TaskDefinition.Settings.StartWhenAvailable can only be false. - /// - /// - /// - /// TaskDefinition.Actions can only contain ExecAction instances unless the TaskDefinition.Actions.PowerShellConversion property has - /// the Version1 flag set. - /// - /// - /// - /// - /// TaskDefinition.Triggers cannot contain CustomTrigger, EventTrigger, SessionStateChangeTrigger, or RegistrationTrigger instances. - /// - /// - /// - /// TaskDefinition.Triggers cannot contain instances with delays set. - /// - /// - /// TaskDefinition.Triggers cannot contain instances with ExecutionTimeLimit or Id properties set. - /// - /// - /// TaskDefinition.Triggers cannot contain LogonTriggers instances with the UserId property set. - /// - /// - /// TaskDefinition.Triggers cannot contain MonthlyDOWTrigger instances with the RunOnLastWeekOfMonth property set to true. - /// - /// - /// TaskDefinition.Triggers cannot contain MonthlyTrigger instances with the RunOnDayWeekOfMonth property set to true. - /// - /// - /// - V1, - - /// - /// The task is compatible with Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). - /// - /// This version is the baseline for the new, non-file based Task Scheduler. See remarks for - /// functionality that was not forward-compatible. - /// - /// - V2, - - /// - /// The task is compatible with Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). - /// Changes from V2: - /// - /// - /// TaskDefinition.Principal.ProcessTokenSidType can be defined as a value other than Default. - /// - /// - /// - /// TaskDefinition.Actions may not contain EmailAction or ShowMessageAction instances unless the - /// TaskDefinition.Actions.PowerShellConversion property has the Version2 flag set. - /// - /// - /// - /// TaskDefinition.Principal.RequiredPrivileges can have privilege values assigned. - /// - /// - /// TaskDefinition.Settings.DisallowStartOnRemoteAppSession can be set to true. - /// - /// - /// TaskDefinition.UseUnifiedSchedulingEngine can be set to true. - /// - /// - /// - V2_1, - - /// - /// The task is compatible with Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). - /// Changes from V2_1: - /// - /// - /// - /// TaskDefinition.Settings.MaintenanceSettings can have Period or Deadline be values other than TimeSpan.Zero or the Exclusive - /// property set to true. - /// - /// - /// - /// TaskDefinition.Settings.Volatile can be set to true. - /// - /// - /// - V2_2, - - /// - /// The task is compatible with Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). - /// Changes from V2_2: - /// - /// - /// None published. - /// - /// - /// - V2_3 - } - - /// Defines how the Task Scheduler service creates, updates, or disables the task. - [DefaultValue(CreateOrUpdate)] - public enum TaskCreation - { - /// The Task Scheduler service registers the task as a new task. - Create = 2, - - /// - /// The Task Scheduler service either registers the task as a new task or as an updated version if the task already exists. - /// Equivalent to Create | Update. - /// - CreateOrUpdate = 6, - - /// - /// The Task Scheduler service registers the disabled task. A disabled task cannot run until it is enabled. For more information, - /// see Enabled Property of TaskSettings and Enabled Property of RegisteredTask. - /// - Disable = 8, - - /// - /// The Task Scheduler service is prevented from adding the allow access-control entry (ACE) for the context principal. When the - /// TaskFolder.RegisterTaskDefinition or TaskFolder.RegisterTask functions are called with this flag to update a task, the Task - /// Scheduler service does not add the ACE for the new context principal and does not remove the ACE from the old context principal. - /// - DontAddPrincipalAce = 0x10, - - /// - /// The Task Scheduler service creates the task, but ignores the registration triggers in the task. By ignoring the registration - /// triggers, the task will not execute when it is registered unless a time-based trigger causes it to execute on registration. - /// - IgnoreRegistrationTriggers = 0x20, - - /// - /// The Task Scheduler service registers the task as an updated version of an existing task. When a task with a registration trigger - /// is updated, the task will execute after the update occurs. - /// - Update = 4, - - /// - /// The Task Scheduler service checks the syntax of the XML that describes the task but does not register the task. This constant - /// cannot be combined with the Create, Update, or CreateOrUpdate values. - /// - ValidateOnly = 1 - } - - /// Defines how the Task Scheduler handles existing instances of the task when it starts a new instance of the task. - [DefaultValue(IgnoreNew)] - public enum TaskInstancesPolicy - { - /// Starts new instance while an existing instance is running. - Parallel, - - /// Starts a new instance of the task after all other instances of the task are complete. - Queue, - - /// Does not start a new instance if an existing instance of the task is running. - IgnoreNew, - - /// Stops an existing instance of the task before it starts a new instance. - StopExisting - } - - /// Defines what logon technique is required to run a task. - [DefaultValue(S4U)] - public enum TaskLogonType - { - /// The logon method is not specified. Used for non-NT credentials. - None, - - /// Use a password for logging on the user. The password must be supplied at registration time. - Password, - - /// - /// Use an existing interactive token to run a task. The user must log on using a service for user (S4U) logon. When an S4U logon is - /// used, no password is stored by the system and there is no access to either the network or to encrypted files. - /// - S4U, - - /// User must already be logged on. The task will be run only in an existing interactive session. - InteractiveToken, - - /// Group activation. The groupId field specifies the group. - Group, - - /// - /// Indicates that a Local System, Local Service, or Network Service account is being used as a security context to run the task. - /// - ServiceAccount, - - /// - /// First use the interactive token. If the user is not logged on (no interactive token is available), then the password is used. - /// The password must be specified when a task is registered. This flag is not recommended for new tasks because it is less reliable - /// than Password. - /// - InteractiveTokenOrPassword - } - - /// Defines which privileges must be required for a secured task. - public enum TaskPrincipalPrivilege - { - /// Required to create a primary token. User Right: Create a token object. - SeCreateTokenPrivilege = 1, - - /// Required to assign the primary token of a process. User Right: Replace a process-level token. - SeAssignPrimaryTokenPrivilege, - - /// Required to lock physical pages in memory. User Right: Lock pages in memory. - SeLockMemoryPrivilege, - - /// Required to increase the quota assigned to a process. User Right: Adjust memory quotas for a process. - SeIncreaseQuotaPrivilege, - - /// Required to read unsolicited input from a terminal device. User Right: Not applicable. - SeUnsolicitedInputPrivilege, - - /// Required to create a computer account. User Right: Add workstations to domain. - SeMachineAccountPrivilege, - - /// - /// This privilege identifies its holder as part of the trusted computer base. Some trusted protected subsystems are granted this - /// privilege. User Right: Act as part of the operating system. - /// - SeTcbPrivilege, - - /// - /// Required to perform a number of security-related functions, such as controlling and viewing audit messages. This privilege - /// identifies its holder as a security operator. User Right: Manage auditing and the security log. - /// - SeSecurityPrivilege, - - /// - /// Required to take ownership of an object without being granted discretionary access. This privilege allows the owner value to be - /// set only to those values that the holder may legitimately assign as the owner of an object. User Right: Take ownership of files - /// or other objects. - /// - SeTakeOwnershipPrivilege, - - /// Required to load or unload a device driver. User Right: Load and unload device drivers. - SeLoadDriverPrivilege, - - /// Required to gather profiling information for the entire system. User Right: Profile system performance. - SeSystemProfilePrivilege, - - /// Required to modify the system time. User Right: Change the system time. - SeSystemtimePrivilege, - - /// Required to gather profiling information for a single process. User Right: Profile single process. - SeProfileSingleProcessPrivilege, - - /// Required to increase the base priority of a process. User Right: Increase scheduling priority. - SeIncreaseBasePriorityPrivilege, - - /// Required to create a paging file. User Right: Create a pagefile. - SeCreatePagefilePrivilege, - - /// Required to create a permanent object. User Right: Create permanent shared objects. - SeCreatePermanentPrivilege, - - /// - /// Required to perform backup operations. This privilege causes the system to grant all read access control to any file, regardless - /// of the access control list (ACL) specified for the file. Any access request other than read is still evaluated with the ACL. - /// This privilege is required by the RegSaveKey and RegSaveKeyExfunctions. The following access rights are granted if this - /// privilege is held: READ_CONTROL, ACCESS_SYSTEM_SECURITY, FILE_GENERIC_READ, FILE_TRAVERSE. User Right: Back up files and directories. - /// - SeBackupPrivilege, - - /// - /// Required to perform restore operations. This privilege causes the system to grant all write access control to any file, - /// regardless of the ACL specified for the file. Any access request other than write is still evaluated with the ACL. Additionally, - /// this privilege enables you to set any valid user or group security identifier (SID) as the owner of a file. This privilege is - /// required by the RegLoadKey function. The following access rights are granted if this privilege is held: WRITE_DAC, WRITE_OWNER, - /// ACCESS_SYSTEM_SECURITY, FILE_GENERIC_WRITE, FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, DELETE. User Right: Restore files and directories. - /// - SeRestorePrivilege, - - /// Required to shut down a local system. User Right: Shut down the system. - SeShutdownPrivilege, - - /// Required to debug and adjust the memory of a process owned by another account. User Right: Debug programs. - SeDebugPrivilege, - - /// Required to generate audit-log entries. Give this privilege to secure servers. User Right: Generate security audits. - SeAuditPrivilege, - - /// - /// Required to modify the nonvolatile RAM of systems that use this type of memory to store configuration information. User Right: - /// Modify firmware environment values. - /// - SeSystemEnvironmentPrivilege, - - /// - /// Required to receive notifications of changes to files or directories. This privilege also causes the system to skip all - /// traversal access checks. It is enabled by default for all users. User Right: Bypass traverse checking. - /// - SeChangeNotifyPrivilege, - - /// Required to shut down a system by using a network request. User Right: Force shutdown from a remote system. - SeRemoteShutdownPrivilege, - - /// Required to undock a laptop. User Right: Remove computer from docking station. - SeUndockPrivilege, - - /// - /// Required for a domain controller to use the LDAP directory synchronization services. This privilege allows the holder to read - /// all objects and properties in the directory, regardless of the protection on the objects and properties. By default, it is - /// assigned to the Administrator and LocalSystem accounts on domain controllers. User Right: Synchronize directory service data. - /// - SeSyncAgentPrivilege, - - /// - /// Required to mark user and computer accounts as trusted for delegation. User Right: Enable computer and user accounts to be - /// trusted for delegation. - /// - SeEnableDelegationPrivilege, - - /// Required to enable volume management privileges. User Right: Manage the files on a volume. - SeManageVolumePrivilege, - - /// - /// Required to impersonate. User Right: Impersonate a client after authentication. Windows XP/2000: This privilege is not - /// supported. Note that this value is supported starting with Windows Server 2003, Windows XP with SP2, and Windows 2000 with SP4. - /// - SeImpersonatePrivilege, - - /// - /// Required to create named file mapping objects in the global namespace during Terminal Services sessions. This privilege is - /// enabled by default for administrators, services, and the local system account. User Right: Create global objects. Windows - /// XP/2000: This privilege is not supported. Note that this value is supported starting with Windows Server 2003, Windows XP with - /// SP2, and Windows 2000 with SP4. - /// - SeCreateGlobalPrivilege, - - /// Required to access Credential Manager as a trusted caller. User Right: Access Credential Manager as a trusted caller. - SeTrustedCredManAccessPrivilege, - - /// Required to modify the mandatory integrity level of an object. User Right: Modify an object label. - SeRelabelPrivilege, - - /// - /// Required to allocate more memory for applications that run in the context of users. User Right: Increase a process working set. - /// - SeIncreaseWorkingSetPrivilege, - - /// Required to adjust the time zone associated with the computer's internal clock. User Right: Change the time zone. - SeTimeZonePrivilege, - - /// Required to create a symbolic link. User Right: Create symbolic links. - SeCreateSymbolicLinkPrivilege - } - - /// - /// Defines the types of process security identifier (SID) that can be used by tasks. These changes are used to specify the type of - /// process SID in the IPrincipal2 interface. - /// - public enum TaskProcessTokenSidType - { - /// No changes will be made to the process token groups list. - None = 0, - - /// - /// A task SID that is derived from the task name will be added to the process token groups list, and the token default - /// discretionary access control list (DACL) will be modified to allow only the task SID and local system full control and the - /// account SID read control. - /// - Unrestricted = 1, - - /// A Task Scheduler will apply default settings to the task process. - Default = 2 - } - - /// Defines how a task is run. - [Flags] - public enum TaskRunFlags - { - /// The task is run with all flags ignored. - NoFlags = 0, - - /// The task is run as the user who is calling the Run method. - AsSelf = 1, - - /// The task is run regardless of constraints such as "do not run on batteries" or "run only if idle". - IgnoreConstraints = 2, - - /// The task is run using a terminal server session identifier. - UseSessionId = 4, - - /// The task is run using a security identifier. - UserSID = 8 - } - - /// Defines LUA elevation flags that specify with what privilege level the task will be run. - public enum TaskRunLevel - { - /// Tasks will be run with the least privileges. - [XmlEnum("LeastPrivilege")] - LUA, - - /// Tasks will be run with the highest privileges. - [XmlEnum("HighestAvailable")] - Highest - } - - /// - /// Defines what kind of Terminal Server session state change you can use to trigger a task to start. These changes are used to specify - /// the type of state change in the SessionStateChangeTrigger. - /// - public enum TaskSessionStateChangeType - { - /// - /// Terminal Server console connection state change. For example, when you connect to a user session on the local computer by - /// switching users on the computer. - /// - ConsoleConnect = 1, - - /// - /// Terminal Server console disconnection state change. For example, when you disconnect to a user session on the local computer by - /// switching users on the computer. - /// - ConsoleDisconnect = 2, - - /// - /// Terminal Server remote connection state change. For example, when a user connects to a user session by using the Remote Desktop - /// Connection program from a remote computer. - /// - RemoteConnect = 3, - - /// - /// Terminal Server remote disconnection state change. For example, when a user disconnects from a user session while using the - /// Remote Desktop Connection program from a remote computer. - /// - RemoteDisconnect = 4, - - /// - /// Terminal Server session locked state change. For example, this state change causes the task to run when the computer is locked. - /// - SessionLock = 7, - - /// - /// Terminal Server session unlocked state change. For example, this state change causes the task to run when the computer is unlocked. - /// - SessionUnlock = 8 - } - - /// Options for use when calling the SetSecurityDescriptorSddlForm methods. - [Flags] - public enum TaskSetSecurityOptions - { - /// No special handling. - None = 0, - - /// The Task Scheduler service is prevented from adding the allow access-control entry (ACE) for the context principal. - DontAddPrincipalAce = 0x10 - } - - /***** WAITING TO DETERMINE USE CASE ***** + { + /// The task is compatible with the AT command. + AT, + + /// + /// The task is compatible with Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). + /// Items not available when compared to V2: + /// + /// + /// TaskDefinition.Principal.GroupId - All account information can be retrieved via the UserId property. + /// + /// + /// TaskLogonType values Group, None and S4U are not supported. + /// + /// + /// TaskDefinition.Principal.RunLevel == TaskRunLevel.Highest is not supported. + /// + /// + /// + /// Assigning access security to a task is not supported using TaskDefinition.RegistrationInfo.SecurityDescriptorSddlForm or in RegisterTaskDefinition. + /// + /// + /// + /// + /// TaskDefinition.RegistrationInfo.Documentation, Source, URI and Version properties are only supported using this library. See + /// details in the remarks for . + /// + /// + /// + /// TaskDefinition.Settings.AllowDemandStart cannot be false. + /// + /// + /// TaskDefinition.Settings.AllowHardTerminate cannot be false. + /// + /// + /// TaskDefinition.Settings.MultipleInstances can only be IgnoreNew. + /// + /// + /// TaskDefinition.Settings.NetworkSettings cannot have any values. + /// + /// + /// TaskDefinition.Settings.RestartCount can only be 0. + /// + /// + /// TaskDefinition.Settings.StartWhenAvailable can only be false. + /// + /// + /// + /// TaskDefinition.Actions can only contain ExecAction instances unless the TaskDefinition.Actions.PowerShellConversion property has + /// the Version1 flag set. + /// + /// + /// + /// + /// TaskDefinition.Triggers cannot contain CustomTrigger, EventTrigger, SessionStateChangeTrigger, or RegistrationTrigger instances. + /// + /// + /// + /// TaskDefinition.Triggers cannot contain instances with delays set. + /// + /// + /// TaskDefinition.Triggers cannot contain instances with ExecutionTimeLimit or Id properties set. + /// + /// + /// TaskDefinition.Triggers cannot contain LogonTriggers instances with the UserId property set. + /// + /// + /// TaskDefinition.Triggers cannot contain MonthlyDOWTrigger instances with the RunOnLastWeekOfMonth property set to true. + /// + /// + /// TaskDefinition.Triggers cannot contain MonthlyTrigger instances with the RunOnDayWeekOfMonth property set to true. + /// + /// + /// + V1, + + /// + /// The task is compatible with Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). + /// + /// This version is the baseline for the new, non-file based Task Scheduler. See remarks for + /// functionality that was not forward-compatible. + /// + /// + V2, + + /// + /// The task is compatible with Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). + /// Changes from V2: + /// + /// + /// TaskDefinition.Principal.ProcessTokenSidType can be defined as a value other than Default. + /// + /// + /// + /// TaskDefinition.Actions may not contain EmailAction or ShowMessageAction instances unless the + /// TaskDefinition.Actions.PowerShellConversion property has the Version2 flag set. + /// + /// + /// + /// TaskDefinition.Principal.RequiredPrivileges can have privilege values assigned. + /// + /// + /// TaskDefinition.Settings.DisallowStartOnRemoteAppSession can be set to true. + /// + /// + /// TaskDefinition.UseUnifiedSchedulingEngine can be set to true. + /// + /// + /// + V2_1, + + /// + /// The task is compatible with Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). + /// Changes from V2_1: + /// + /// + /// + /// TaskDefinition.Settings.MaintenanceSettings can have Period or Deadline be values other than TimeSpan.Zero or the Exclusive + /// property set to true. + /// + /// + /// + /// TaskDefinition.Settings.Volatile can be set to true. + /// + /// + /// + V2_2, + + /// + /// The task is compatible with Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). + /// Changes from V2_2: + /// + /// + /// None published. + /// + /// + /// + V2_3 + } + + /// Defines how the Task Scheduler service creates, updates, or disables the task. + [DefaultValue(CreateOrUpdate)] + public enum TaskCreation + { + /// The Task Scheduler service registers the task as a new task. + Create = 2, + + /// + /// The Task Scheduler service either registers the task as a new task or as an updated version if the task already exists. + /// Equivalent to Create | Update. + /// + CreateOrUpdate = 6, + + /// + /// The Task Scheduler service registers the disabled task. A disabled task cannot run until it is enabled. For more information, + /// see Enabled Property of TaskSettings and Enabled Property of RegisteredTask. + /// + Disable = 8, + + /// + /// The Task Scheduler service is prevented from adding the allow access-control entry (ACE) for the context principal. When the + /// TaskFolder.RegisterTaskDefinition or TaskFolder.RegisterTask functions are called with this flag to update a task, the Task + /// Scheduler service does not add the ACE for the new context principal and does not remove the ACE from the old context principal. + /// + DontAddPrincipalAce = 0x10, + + /// + /// The Task Scheduler service creates the task, but ignores the registration triggers in the task. By ignoring the registration + /// triggers, the task will not execute when it is registered unless a time-based trigger causes it to execute on registration. + /// + IgnoreRegistrationTriggers = 0x20, + + /// + /// The Task Scheduler service registers the task as an updated version of an existing task. When a task with a registration trigger + /// is updated, the task will execute after the update occurs. + /// + Update = 4, + + /// + /// The Task Scheduler service checks the syntax of the XML that describes the task but does not register the task. This constant + /// cannot be combined with the Create, Update, or CreateOrUpdate values. + /// + ValidateOnly = 1 + } + + /// Defines how the Task Scheduler handles existing instances of the task when it starts a new instance of the task. + [DefaultValue(IgnoreNew)] + public enum TaskInstancesPolicy + { + /// Starts new instance while an existing instance is running. + Parallel, + + /// Starts a new instance of the task after all other instances of the task are complete. + Queue, + + /// Does not start a new instance if an existing instance of the task is running. + IgnoreNew, + + /// Stops an existing instance of the task before it starts a new instance. + StopExisting + } + + /// Defines what logon technique is required to run a task. + [DefaultValue(S4U)] + public enum TaskLogonType + { + /// The logon method is not specified. Used for non-NT credentials. + None, + + /// Use a password for logging on the user. The password must be supplied at registration time. + Password, + + /// + /// Use an existing interactive token to run a task. The user must log on using a service for user (S4U) logon. When an S4U logon is + /// used, no password is stored by the system and there is no access to either the network or to encrypted files. + /// + S4U, + + /// User must already be logged on. The task will be run only in an existing interactive session. + InteractiveToken, + + /// Group activation. The groupId field specifies the group. + Group, + + /// + /// Indicates that a Local System, Local Service, or Network Service account is being used as a security context to run the task. + /// + ServiceAccount, + + /// + /// First use the interactive token. If the user is not logged on (no interactive token is available), then the password is used. + /// The password must be specified when a task is registered. This flag is not recommended for new tasks because it is less reliable + /// than Password. + /// + InteractiveTokenOrPassword + } + + /// Defines which privileges must be required for a secured task. + public enum TaskPrincipalPrivilege + { + /// Required to create a primary token. User Right: Create a token object. + SeCreateTokenPrivilege = 1, + + /// Required to assign the primary token of a process. User Right: Replace a process-level token. + SeAssignPrimaryTokenPrivilege, + + /// Required to lock physical pages in memory. User Right: Lock pages in memory. + SeLockMemoryPrivilege, + + /// Required to increase the quota assigned to a process. User Right: Adjust memory quotas for a process. + SeIncreaseQuotaPrivilege, + + /// Required to read unsolicited input from a terminal device. User Right: Not applicable. + SeUnsolicitedInputPrivilege, + + /// Required to create a computer account. User Right: Add workstations to domain. + SeMachineAccountPrivilege, + + /// + /// This privilege identifies its holder as part of the trusted computer base. Some trusted protected subsystems are granted this + /// privilege. User Right: Act as part of the operating system. + /// + SeTcbPrivilege, + + /// + /// Required to perform a number of security-related functions, such as controlling and viewing audit messages. This privilege + /// identifies its holder as a security operator. User Right: Manage auditing and the security log. + /// + SeSecurityPrivilege, + + /// + /// Required to take ownership of an object without being granted discretionary access. This privilege allows the owner value to be + /// set only to those values that the holder may legitimately assign as the owner of an object. User Right: Take ownership of files + /// or other objects. + /// + SeTakeOwnershipPrivilege, + + /// Required to load or unload a device driver. User Right: Load and unload device drivers. + SeLoadDriverPrivilege, + + /// Required to gather profiling information for the entire system. User Right: Profile system performance. + SeSystemProfilePrivilege, + + /// Required to modify the system time. User Right: Change the system time. + SeSystemtimePrivilege, + + /// Required to gather profiling information for a single process. User Right: Profile single process. + SeProfileSingleProcessPrivilege, + + /// Required to increase the base priority of a process. User Right: Increase scheduling priority. + SeIncreaseBasePriorityPrivilege, + + /// Required to create a paging file. User Right: Create a pagefile. + SeCreatePagefilePrivilege, + + /// Required to create a permanent object. User Right: Create permanent shared objects. + SeCreatePermanentPrivilege, + + /// + /// Required to perform backup operations. This privilege causes the system to grant all read access control to any file, regardless + /// of the access control list (ACL) specified for the file. Any access request other than read is still evaluated with the ACL. + /// This privilege is required by the RegSaveKey and RegSaveKeyExfunctions. The following access rights are granted if this + /// privilege is held: READ_CONTROL, ACCESS_SYSTEM_SECURITY, FILE_GENERIC_READ, FILE_TRAVERSE. User Right: Back up files and directories. + /// + SeBackupPrivilege, + + /// + /// Required to perform restore operations. This privilege causes the system to grant all write access control to any file, + /// regardless of the ACL specified for the file. Any access request other than write is still evaluated with the ACL. Additionally, + /// this privilege enables you to set any valid user or group security identifier (SID) as the owner of a file. This privilege is + /// required by the RegLoadKey function. The following access rights are granted if this privilege is held: WRITE_DAC, WRITE_OWNER, + /// ACCESS_SYSTEM_SECURITY, FILE_GENERIC_WRITE, FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, DELETE. User Right: Restore files and directories. + /// + SeRestorePrivilege, + + /// Required to shut down a local system. User Right: Shut down the system. + SeShutdownPrivilege, + + /// Required to debug and adjust the memory of a process owned by another account. User Right: Debug programs. + SeDebugPrivilege, + + /// Required to generate audit-log entries. Give this privilege to secure servers. User Right: Generate security audits. + SeAuditPrivilege, + + /// + /// Required to modify the nonvolatile RAM of systems that use this type of memory to store configuration information. User Right: + /// Modify firmware environment values. + /// + SeSystemEnvironmentPrivilege, + + /// + /// Required to receive notifications of changes to files or directories. This privilege also causes the system to skip all + /// traversal access checks. It is enabled by default for all users. User Right: Bypass traverse checking. + /// + SeChangeNotifyPrivilege, + + /// Required to shut down a system by using a network request. User Right: Force shutdown from a remote system. + SeRemoteShutdownPrivilege, + + /// Required to undock a laptop. User Right: Remove computer from docking station. + SeUndockPrivilege, + + /// + /// Required for a domain controller to use the LDAP directory synchronization services. This privilege allows the holder to read + /// all objects and properties in the directory, regardless of the protection on the objects and properties. By default, it is + /// assigned to the Administrator and LocalSystem accounts on domain controllers. User Right: Synchronize directory service data. + /// + SeSyncAgentPrivilege, + + /// + /// Required to mark user and computer accounts as trusted for delegation. User Right: Enable computer and user accounts to be + /// trusted for delegation. + /// + SeEnableDelegationPrivilege, + + /// Required to enable volume management privileges. User Right: Manage the files on a volume. + SeManageVolumePrivilege, + + /// + /// Required to impersonate. User Right: Impersonate a client after authentication. Windows XP/2000: This privilege is not + /// supported. Note that this value is supported starting with Windows Server 2003, Windows XP with SP2, and Windows 2000 with SP4. + /// + SeImpersonatePrivilege, + + /// + /// Required to create named file mapping objects in the global namespace during Terminal Services sessions. This privilege is + /// enabled by default for administrators, services, and the local system account. User Right: Create global objects. Windows + /// XP/2000: This privilege is not supported. Note that this value is supported starting with Windows Server 2003, Windows XP with + /// SP2, and Windows 2000 with SP4. + /// + SeCreateGlobalPrivilege, + + /// Required to access Credential Manager as a trusted caller. User Right: Access Credential Manager as a trusted caller. + SeTrustedCredManAccessPrivilege, + + /// Required to modify the mandatory integrity level of an object. User Right: Modify an object label. + SeRelabelPrivilege, + + /// + /// Required to allocate more memory for applications that run in the context of users. User Right: Increase a process working set. + /// + SeIncreaseWorkingSetPrivilege, + + /// Required to adjust the time zone associated with the computer's internal clock. User Right: Change the time zone. + SeTimeZonePrivilege, + + /// Required to create a symbolic link. User Right: Create symbolic links. + SeCreateSymbolicLinkPrivilege + } + + /// + /// Defines the types of process security identifier (SID) that can be used by tasks. These changes are used to specify the type of + /// process SID in the IPrincipal2 interface. + /// + public enum TaskProcessTokenSidType + { + /// No changes will be made to the process token groups list. + None = 0, + + /// + /// A task SID that is derived from the task name will be added to the process token groups list, and the token default + /// discretionary access control list (DACL) will be modified to allow only the task SID and local system full control and the + /// account SID read control. + /// + Unrestricted = 1, + + /// A Task Scheduler will apply default settings to the task process. + Default = 2 + } + + /// Defines how a task is run. + [Flags] + public enum TaskRunFlags + { + /// The task is run with all flags ignored. + NoFlags = 0, + + /// The task is run as the user who is calling the Run method. + AsSelf = 1, + + /// The task is run regardless of constraints such as "do not run on batteries" or "run only if idle". + IgnoreConstraints = 2, + + /// The task is run using a terminal server session identifier. + UseSessionId = 4, + + /// The task is run using a security identifier. + UserSID = 8 + } + + /// Defines LUA elevation flags that specify with what privilege level the task will be run. + public enum TaskRunLevel + { + /// Tasks will be run with the least privileges. + [XmlEnum("LeastPrivilege")] + LUA, + + /// Tasks will be run with the highest privileges. + [XmlEnum("HighestAvailable")] + Highest + } + + /// + /// Defines what kind of Terminal Server session state change you can use to trigger a task to start. These changes are used to specify + /// the type of state change in the SessionStateChangeTrigger. + /// + public enum TaskSessionStateChangeType + { + /// + /// Terminal Server console connection state change. For example, when you connect to a user session on the local computer by + /// switching users on the computer. + /// + ConsoleConnect = 1, + + /// + /// Terminal Server console disconnection state change. For example, when you disconnect to a user session on the local computer by + /// switching users on the computer. + /// + ConsoleDisconnect = 2, + + /// + /// Terminal Server remote connection state change. For example, when a user connects to a user session by using the Remote Desktop + /// Connection program from a remote computer. + /// + RemoteConnect = 3, + + /// + /// Terminal Server remote disconnection state change. For example, when a user disconnects from a user session while using the + /// Remote Desktop Connection program from a remote computer. + /// + RemoteDisconnect = 4, + + /// + /// Terminal Server session locked state change. For example, this state change causes the task to run when the computer is locked. + /// + SessionLock = 7, + + /// + /// Terminal Server session unlocked state change. For example, this state change causes the task to run when the computer is unlocked. + /// + SessionUnlock = 8 + } + + /// Options for use when calling the SetSecurityDescriptorSddlForm methods. + [Flags] + public enum TaskSetSecurityOptions + { + /// No special handling. + None = 0, + + /// The Task Scheduler service is prevented from adding the allow access-control entry (ACE) for the context principal. + DontAddPrincipalAce = 0x10 + } + + /***** WAITING TO DETERMINE USE CASE ***** /// Success and error codes that some methods will expose through . public enum TaskResultCode { @@ -614,3124 +614,3124 @@ public enum TaskResultCode } */ - /// Defines the different states that a registered task can be in. - public enum TaskState - { - /// The state of the task is unknown. - Unknown, - - /// - /// The task is registered but is disabled and no instances of the task are queued or running. The task cannot be run until it is enabled. - /// - Disabled, - - /// Instances of the task are queued. - Queued, - - /// The task is ready to be executed, but no instances are queued or running. - Ready, - - /// One or more instances of the task is running. - Running - } - - /// - /// Specifies how the Task Scheduler performs tasks when the computer is in an idle condition. For information about idle conditions, - /// see Task Idle Conditions. - /// - [PublicAPI] - public sealed class IdleSettings : IDisposable, INotifyPropertyChanged - { - private readonly IIdleSettings v2Settings; - private ITask v1Task; - - internal IdleSettings([NotNull] IIdleSettings iSettings) => v2Settings = iSettings; - - internal IdleSettings([NotNull] ITask iTask) => v1Task = iTask; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Gets or sets a value that indicates the amount of time that the computer must be in an idle state before the task is run. - /// - /// - /// A value that indicates the amount of time that the computer must be in an idle state before the task is run. The minimum value - /// is one minute. If this value is TimeSpan.Zero, then the delay will be set to the default of 10 minutes. - /// - [DefaultValue(typeof(TimeSpan), "00:10:00")] - [XmlElement("Duration")] - public TimeSpan IdleDuration - { - get - { - if (v2Settings != null) - return Task.StringToTimeSpan(v2Settings.IdleDuration); - v1Task.GetIdleWait(out var _, out var deadMin); - return TimeSpan.FromMinutes(deadMin); - } - set - { - if (v2Settings != null) - { - if (value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) - throw new ArgumentOutOfRangeException(nameof(IdleDuration)); - v2Settings.IdleDuration = Task.TimeSpanToString(value); - } - else - { - v1Task.SetIdleWait((ushort)WaitTimeout.TotalMinutes, (ushort)value.TotalMinutes); - } - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates whether the task is restarted when the computer cycles into an idle condition more - /// than once. - /// - [DefaultValue(false)] - public bool RestartOnIdle - { - get => v2Settings?.RestartOnIdle ?? v1Task.HasFlags(TaskFlags.RestartOnIdleResume); - set - { - if (v2Settings != null) - v2Settings.RestartOnIdle = value; - else - v1Task.SetFlags(TaskFlags.RestartOnIdleResume, value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the Task Scheduler will terminate the task if the idle condition ends before - /// the task is completed. - /// - [DefaultValue(true)] - public bool StopOnIdleEnd - { - get => v2Settings?.StopOnIdleEnd ?? v1Task.HasFlags(TaskFlags.KillOnIdleEnd); - set + /// Defines the different states that a registered task can be in. + public enum TaskState + { + /// The state of the task is unknown. + Unknown, + + /// + /// The task is registered but is disabled and no instances of the task are queued or running. The task cannot be run until it is enabled. + /// + Disabled, + + /// Instances of the task are queued. + Queued, + + /// The task is ready to be executed, but no instances are queued or running. + Ready, + + /// One or more instances of the task is running. + Running + } + + /// + /// Specifies how the Task Scheduler performs tasks when the computer is in an idle condition. For information about idle conditions, + /// see Task Idle Conditions. + /// + [PublicAPI] + public sealed class IdleSettings : IDisposable, INotifyPropertyChanged + { + private readonly IIdleSettings v2Settings; + private ITask v1Task; + + internal IdleSettings([NotNull] IIdleSettings iSettings) => v2Settings = iSettings; + + internal IdleSettings([NotNull] ITask iTask) => v1Task = iTask; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Gets or sets a value that indicates the amount of time that the computer must be in an idle state before the task is run. + /// + /// + /// A value that indicates the amount of time that the computer must be in an idle state before the task is run. The minimum value + /// is one minute. If this value is TimeSpan.Zero, then the delay will be set to the default of 10 minutes. + /// + [DefaultValue(typeof(TimeSpan), "00:10:00")] + [XmlElement("Duration")] + public TimeSpan IdleDuration + { + get + { + if (v2Settings != null) + return Task.StringToTimeSpan(v2Settings.IdleDuration); + v1Task.GetIdleWait(out var _, out var deadMin); + return TimeSpan.FromMinutes(deadMin); + } + set + { + if (v2Settings != null) + { + if (value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) + throw new ArgumentOutOfRangeException(nameof(IdleDuration)); + v2Settings.IdleDuration = Task.TimeSpanToString(value); + } + else + { + v1Task.SetIdleWait((ushort)WaitTimeout.TotalMinutes, (ushort)value.TotalMinutes); + } + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates whether the task is restarted when the computer cycles into an idle condition more + /// than once. + /// + [DefaultValue(false)] + public bool RestartOnIdle + { + get => v2Settings?.RestartOnIdle ?? v1Task.HasFlags(TaskFlags.RestartOnIdleResume); + set + { + if (v2Settings != null) + v2Settings.RestartOnIdle = value; + else + v1Task.SetFlags(TaskFlags.RestartOnIdleResume, value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the Task Scheduler will terminate the task if the idle condition ends before + /// the task is completed. + /// + [DefaultValue(true)] + public bool StopOnIdleEnd + { + get => v2Settings?.StopOnIdleEnd ?? v1Task.HasFlags(TaskFlags.KillOnIdleEnd); + set + { + if (v2Settings != null) + v2Settings.StopOnIdleEnd = value; + else + v1Task.SetFlags(TaskFlags.KillOnIdleEnd, value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a value that indicates the amount of time that the Task Scheduler will wait for an idle condition to occur. If no + /// value is specified for this property, then the Task Scheduler service will wait indefinitely for an idle condition to occur. + /// + /// + /// A value that indicates the amount of time that the Task Scheduler will wait for an idle condition to occur. The minimum time + /// allowed is 1 minute. If this value is TimeSpan.Zero, then the delay will be set to the default of 1 hour. + /// + [DefaultValue(typeof(TimeSpan), "01:00:00")] + public TimeSpan WaitTimeout + { + get + { + if (v2Settings != null) + return Task.StringToTimeSpan(v2Settings.WaitTimeout); + v1Task.GetIdleWait(out var idleMin, out var _); + return TimeSpan.FromMinutes(idleMin); + } + set + { + if (v2Settings != null) + { + if (value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) + throw new ArgumentOutOfRangeException(nameof(WaitTimeout)); + v2Settings.WaitTimeout = Task.TimeSpanToString(value); + } + else + { + v1Task.SetIdleWait((ushort)value.TotalMinutes, (ushort)IdleDuration.TotalMinutes); + } + OnNotifyPropertyChanged(); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Settings != null) + Marshal.ReleaseComObject(v2Settings); + v1Task = null; + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() + { + if (v2Settings != null || v1Task != null) + return DebugHelper.GetDebugString(this); + return base.ToString(); + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// Specifies the task settings the Task scheduler will use to start task during Automatic maintenance. + [XmlType(IncludeInSchema = false)] + [PublicAPI] + public sealed class MaintenanceSettings : IDisposable, INotifyPropertyChanged + { + private readonly ITaskSettings3 iSettings; + private IMaintenanceSettings iMaintSettings; + + internal MaintenanceSettings([CanBeNull] ITaskSettings3 iSettings3) + { + iSettings = iSettings3; + if (iSettings3 != null) + iMaintSettings = iSettings.MaintenanceSettings; + } + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Gets or sets the amount of time after which the Task scheduler attempts to run the task during emergency Automatic maintenance, + /// if the task failed to complete during regular Automatic maintenance. The minimum value is one day. The value of the property should be greater than the value of the property. If the deadline is not + /// specified the task will not be started during emergency Automatic maintenance. + /// + /// Property set for a task on a Task Scheduler version prior to 2.2. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + public TimeSpan Deadline + { + get => iMaintSettings != null ? Task.StringToTimeSpan(iMaintSettings.Deadline) : TimeSpan.Zero; + set + { + if (iSettings != null) + { + if (iMaintSettings == null && value != TimeSpan.Zero) + iMaintSettings = iSettings.CreateMaintenanceSettings(); + if (iMaintSettings != null) + iMaintSettings.Deadline = Task.TimeSpanToString(value); + } + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_2); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a value indicating whether the Task Scheduler must start the task during the Automatic maintenance in exclusive + /// mode. The exclusivity is guaranteed only between other maintenance tasks and doesn't grant any ordering priority of the task. If + /// exclusivity is not specified, the task is started in parallel with other maintenance tasks. + /// + /// Property set for a task on a Task Scheduler version prior to 2.2. + [DefaultValue(false)] + public bool Exclusive + { + get => iMaintSettings != null && iMaintSettings.Exclusive; + set + { + if (iSettings != null) + { + if (iMaintSettings == null && value) + iMaintSettings = iSettings.CreateMaintenanceSettings(); + if (iMaintSettings != null) + iMaintSettings.Exclusive = value; + } + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_2); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the amount of time the task needs to be started during Automatic maintenance. The minimum value is one minute. + /// + /// Property set for a task on a Task Scheduler version prior to 2.2. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + public TimeSpan Period + { + get => iMaintSettings != null ? Task.StringToTimeSpan(iMaintSettings.Period) : TimeSpan.Zero; + set + { + if (iSettings != null) + { + if (iMaintSettings == null && value != TimeSpan.Zero) + iMaintSettings = iSettings.CreateMaintenanceSettings(); + if (iMaintSettings != null) + iMaintSettings.Period = Task.TimeSpanToString(value); + } + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_2); + OnNotifyPropertyChanged(); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (iMaintSettings != null) + Marshal.ReleaseComObject(iMaintSettings); + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() => iMaintSettings != null ? DebugHelper.GetDebugString(this) : base.ToString(); + + internal bool IsSet() => iMaintSettings != null && (iMaintSettings.Period != null || iMaintSettings.Deadline != null || iMaintSettings.Exclusive); + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// Provides the settings that the Task Scheduler service uses to obtain a network profile. + [XmlType(IncludeInSchema = false)] + [PublicAPI] + public sealed class NetworkSettings : IDisposable, INotifyPropertyChanged + { + private readonly INetworkSettings v2Settings; + + internal NetworkSettings([CanBeNull] INetworkSettings iSettings) => v2Settings = iSettings; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets or sets a GUID value that identifies a network profile. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(Guid), "00000000-0000-0000-0000-000000000000")] + public Guid Id + { + get + { + string id = null; + if (v2Settings != null) + id = v2Settings.Id; + return string.IsNullOrEmpty(id) ? Guid.Empty : new Guid(id); + } + set + { + if (v2Settings != null) + v2Settings.Id = value == Guid.Empty ? null : value.ToString(); + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the name of a network profile. The name is used for display purposes. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + public string Name + { + get => v2Settings?.Name; + set + { + if (v2Settings != null) + v2Settings.Name = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Settings != null) + Marshal.ReleaseComObject(v2Settings); + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() + { + if (v2Settings != null) + return DebugHelper.GetDebugString(this); + return base.ToString(); + } + + internal bool IsSet() => v2Settings != null && (!string.IsNullOrEmpty(v2Settings.Id) || !string.IsNullOrEmpty(v2Settings.Name)); + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// Provides the methods to get information from and control a running task. + [XmlType(IncludeInSchema = false)] + [PublicAPI] + public sealed class RunningTask : Task + { + private readonly IRunningTask v2RunningTask; + + internal RunningTask([NotNull] TaskService svc, [NotNull] IRegisteredTask iTask, [NotNull] IRunningTask iRunningTask) + : base(svc, iTask) => v2RunningTask = iRunningTask; + + internal RunningTask([NotNull] TaskService svc, [NotNull] ITask iTask) + : base(svc, iTask) + { + } + + /// Gets the process ID for the engine (process) which is running the task. + /// Not supported under Task Scheduler 1.0. + public uint EnginePID + { + get + { + if (v2RunningTask != null) + return v2RunningTask.EnginePID; + throw new NotV1SupportedException(); + } + } + + /// Gets the name of the current action that the running task is performing. + public string CurrentAction => v2RunningTask != null ? v2RunningTask.CurrentAction : v1Task.GetApplicationName(); + + /// Gets the GUID identifier for this instance of the task. + public Guid InstanceGuid => v2RunningTask != null ? new Guid(v2RunningTask.InstanceGuid) : Guid.Empty; + + /// Gets the operational state of the running task. + public override TaskState State => v2RunningTask?.State ?? base.State; + + /// Releases all resources used by this class. + public new void Dispose() + { + base.Dispose(); + if (v2RunningTask != null) Marshal.ReleaseComObject(v2RunningTask); + } + + /// Refreshes all of the local instance variables of the task. + /// Thrown if task is no longer running. + public void Refresh() + { + try { v2RunningTask?.Refresh(); } + catch (COMException ce) when ((uint)ce.ErrorCode == 0x8004130B) + { + throw new InvalidOperationException("The current task is no longer running.", ce); + } + } + } + + /// + /// Provides the methods that are used to run the task immediately, get any running instances of the task, get or set the credentials + /// that are used to register the task, and the properties that describe the task. + /// + [XmlType(IncludeInSchema = false)] + [PublicAPI] + public class Task : IDisposable, IComparable, IComparable, INotifyPropertyChanged + { + internal const AccessControlSections defaultAccessControlSections = AccessControlSections.Owner | AccessControlSections.Group | AccessControlSections.Access; + internal const SecurityInfos defaultSecurityInfosSections = SecurityInfos.Owner | SecurityInfos.Group | SecurityInfos.DiscretionaryAcl; + internal ITask v1Task; + + private static readonly int osLibMinorVer = GetOSLibraryMinorVersion(); + private static readonly DateTime v2InvalidDate = new DateTime(1899, 12, 30); + private readonly IRegisteredTask v2Task; + private TaskDefinition myTD; + + internal Task([NotNull] TaskService svc, [NotNull] ITask iTask) + { + TaskService = svc; + v1Task = iTask; + ReadOnly = false; + } + + internal Task([NotNull] TaskService svc, [NotNull] IRegisteredTask iTask, ITaskDefinition iDef = null) + { + TaskService = svc; + v2Task = iTask; + if (iDef != null) + myTD = new TaskDefinition(iDef); + ReadOnly = false; + } + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets the definition of the task. + [NotNull] + public TaskDefinition Definition => myTD ??= v2Task != null ? new TaskDefinition(GetV2Definition(TaskService, v2Task, true)) : new TaskDefinition(v1Task, Name); + + /// Gets or sets a Boolean value that indicates if the registered task is enabled. + /// + /// As of version 1.8.1, under V1 systems (prior to Vista), this property will immediately update the Disabled state and re-save the + /// current task. If changes have been made to the , then those changes will be saved. + /// + public bool Enabled + { + get => v2Task?.Enabled ?? Definition.Settings.Enabled; + set + { + if (v2Task != null) + v2Task.Enabled = value; + else + { + Definition.Settings.Enabled = value; + Definition.V1Save(null); + } + OnNotifyPropertyChanged(); + } + } + + /// Gets an instance of the parent folder. + /// A object representing the parent folder of this task. + [NotNull] + public TaskFolder Folder + { + get + { + if (v2Task == null) + return TaskService.RootFolder; + + var path = v2Task.Path; + var parentPath = System.IO.Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(parentPath) || parentPath == TaskFolder.rootString) + return TaskService.RootFolder; + return TaskService.GetFolder(parentPath); + } + } + + /// Gets a value indicating whether this task instance is active. + /// true if this task instance is active; otherwise, false. + public bool IsActive + { + get + { + var now = DateTime.Now; + if (!Definition.Settings.Enabled) return false; + foreach (var trigger in Definition.Triggers) + { + if (!trigger.Enabled || now < trigger.StartBoundary || now > trigger.EndBoundary) continue; + if (!(trigger is ICalendarTrigger) || DateTime.MinValue != NextRunTime || trigger is TimeTrigger) + return true; + } + return false; + } + } + + /// Gets the time the registered task was last run. + /// Returns if there are no prior run times. + public DateTime LastRunTime + { + get + { + if (v2Task == null) return v1Task.GetMostRecentRunTime(); + var dt = v2Task.LastRunTime; + return dt == v2InvalidDate ? DateTime.MinValue : dt; + } + } + + /// Gets the results that were returned the last time the registered task was run. + /// The value returned is the last exit code of the last program run via an . + /// + /// + /// + /// + /// + public int LastTaskResult + { + get + { + if (v2Task != null) + return v2Task.LastTaskResult; + return (int)v1Task.GetExitCode(); + } + } + + /// Gets the time when the registered task is next scheduled to run. + /// Returns if there are no future run times. + /// + /// Potentially breaking change in release 1.8.2. For Task Scheduler 2.0, the return value prior to 1.8.2 would be Dec 30, 1899 if + /// there were no future run times. For 1.0, that value would have been DateTime.MinValue. In release 1.8.2 and later, all + /// versions will return DateTime.MinValue if there are no future run times. While this is different from the native 2.0 + /// library, it was deemed more appropriate to have consistency between the two libraries and with other .NET libraries. + /// + public DateTime NextRunTime + { + get + { + if (v2Task == null) return v1Task.GetNextRunTime(); + var ret = v2Task.NextRunTime; + if (ret != DateTime.MinValue && ret != v2InvalidDate) return ret == v2InvalidDate ? DateTime.MinValue : ret; + var nrts = GetRunTimes(DateTime.Now, DateTime.MaxValue, 1); + ret = nrts.Length > 0 ? nrts[0] : DateTime.MinValue; + return ret == v2InvalidDate ? DateTime.MinValue : ret; + } + } + + /// + /// Gets a value indicating whether this task is read only. Only available if is true. + /// + /// true if read only; otherwise, false. + public bool ReadOnly { get; internal set; } + + /// Gets or sets the security descriptor for the task. + /// The security descriptor. + [Obsolete("This property will be removed in deference to the GetAccessControl, GetSecurityDescriptorSddlForm, SetAccessControl and SetSecurityDescriptorSddlForm methods.")] + public GenericSecurityDescriptor SecurityDescriptor + { + get + { + var sddl = GetSecurityDescriptorSddlForm(); + return new RawSecurityDescriptor(sddl); + } + set => SetSecurityDescriptorSddlForm(value.GetSddlForm(defaultAccessControlSections)); + } + + /// Gets the operational state of the registered task. + public virtual TaskState State + { + get + { + if (v2Task != null) + return v2Task.State; + + V1Reactivate(); + if (!Enabled) + return TaskState.Disabled; + switch (v1Task.GetStatus()) + { + case TaskStatus.Ready: + case TaskStatus.NeverRun: + case TaskStatus.NoMoreRuns: + case TaskStatus.Terminated: + return TaskState.Ready; + + case TaskStatus.Running: + return TaskState.Running; + + case TaskStatus.Disabled: + return TaskState.Disabled; + // case TaskStatus.NotScheduled: case TaskStatus.NoTriggers: case TaskStatus.NoTriggerTime: + default: + return TaskState.Unknown; + } + } + } + + /// Gets or sets the that manages this task. + /// The task service. + public TaskService TaskService { get; } + + /// Gets the name of the registered task. + [NotNull] + public string Name => v2Task != null ? v2Task.Name : System.IO.Path.GetFileNameWithoutExtension(GetV1Path(v1Task)); + + /// Gets the number of times the registered task has missed a scheduled run. + /// Not supported under Task Scheduler 1.0. + public int NumberOfMissedRuns => v2Task?.NumberOfMissedRuns ?? throw new NotV1SupportedException(); + + /// Gets the path to where the registered task is stored. + [NotNull] + public string Path => v2Task != null ? v2Task.Path : "\\" + Name; + + /// Gets the XML-formatted registration information for the registered task. + public string Xml => v2Task != null ? v2Task.Xml : Definition.XmlText; + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current + /// instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// + /// An object to compare with this instance. + /// A value that indicates the relative order of the objects being compared. + public int CompareTo(Task other) => string.Compare(Path, other?.Path, StringComparison.InvariantCulture); + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Task != null) + Marshal.ReleaseComObject(v2Task); + v1Task = null; + } + + /// Exports the task to the specified file in XML. + /// Name of the output file. + public void Export([NotNull] string outputFileName) => File.WriteAllText(outputFileName, Xml, Encoding.Unicode); + + /// + /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task + /// described by the current object. + /// + /// A object that encapsulates the access control rules for the current task. + public TaskSecurity GetAccessControl() => GetAccessControl(defaultAccessControlSections); + + /// + /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task + /// described by the current object. + /// + /// + /// One of the values that specifies which group of access control + /// entries to retrieve. + /// + /// A object that encapsulates the access control rules for the current task. + public TaskSecurity GetAccessControl(AccessControlSections includeSections) => new TaskSecurity(this, includeSections); + + /// Gets all instances of the currently running registered task. + /// A with all instances of current task. + /// Not supported under Task Scheduler 1.0. + [NotNull, ItemNotNull] + public RunningTaskCollection GetInstances() => v2Task != null + ? new RunningTaskCollection(TaskService, v2Task.GetInstances(0)) + : throw new NotV1SupportedException(); + + /// + /// Gets the last registration time, looking first at the value and then looking for the + /// most recent registration event in the Event Log. + /// + /// of the last registration or if no value can be found. + public DateTime GetLastRegistrationTime() + { + var ret = Definition.RegistrationInfo.Date; + if (ret != DateTime.MinValue) return ret; + var log = new TaskEventLog(Path, new[] { (int)StandardTaskEventId.JobRegistered }, null, TaskService.TargetServer, TaskService.UserAccountDomain, TaskService.UserName, TaskService.UserPassword); + if (!log.Enabled) return ret; + foreach (var item in log) + { + if (item.TimeCreated.HasValue) + return item.TimeCreated.Value; + } + return ret; + } + + /// Gets the times that the registered task is scheduled to run during a specified time. + /// The starting time for the query. + /// The ending time for the query. + /// The requested number of runs. A value of 0 will return all times requested. + /// The scheduled times that the task will run. + [NotNull] + public DateTime[] GetRunTimes(DateTime start, DateTime end, uint count = 0) + { + const ushort TASK_MAX_RUN_TIMES = 1440; + + NativeMethods.SYSTEMTIME stStart = start; + NativeMethods.SYSTEMTIME stEnd = end; + var runTimes = IntPtr.Zero; + var ret = new DateTime[0]; + try + { + if (v2Task != null) + v2Task.GetRunTimes(ref stStart, ref stEnd, ref count, ref runTimes); + else + { + var count1 = count > 0 && count <= TASK_MAX_RUN_TIMES ? (ushort)count : TASK_MAX_RUN_TIMES; + v1Task.GetRunTimes(ref stStart, ref stEnd, ref count1, ref runTimes); + count = count1; + } + ret = InteropUtil.ToArray(runTimes, (int)count); + } + catch (Exception ex) + { + Debug.WriteLine($"Task.GetRunTimes failed: Error {ex}."); + } + finally + { + Marshal.FreeCoTaskMem(runTimes); + } + Debug.WriteLine($"Task.GetRunTimes ({(v2Task != null ? "V2" : "V1")}): Returned {count} items from {stStart} to {stEnd}."); + return ret; + } + + /// Gets the security descriptor for the task. Not available to Task Scheduler 1.0. + /// Section(s) of the security descriptor to return. + /// The security descriptor for the task. + /// Not supported under Task Scheduler 1.0. + public string GetSecurityDescriptorSddlForm(SecurityInfos includeSections = defaultSecurityInfosSections) => v2Task != null ? v2Task.GetSecurityDescriptor((int)includeSections) : throw new NotV1SupportedException(); + + /// + /// Updates the task with any changes made to the by calling from the currently registered folder using the currently + /// registered name. + /// + /// Thrown if task was previously registered with a password. + public void RegisterChanges() + { + if (Definition.Principal.RequiresPassword()) + throw new SecurityException("Tasks which have been registered previously with stored passwords must use the TaskFolder.RegisterTaskDefinition method for updates."); + if (v2Task != null) + TaskService.GetFolder(System.IO.Path.GetDirectoryName(Path)).RegisterTaskDefinition(Name, Definition, TaskCreation.Update, null, null, Definition.Principal.LogonType); + else + TaskService.RootFolder.RegisterTaskDefinition(Name, Definition); + } + + /// Runs the registered task immediately. + /// + /// + /// The parameters used as values in the task actions. A maximum of 32 parameters can be supplied. To run a task with no parameters, + /// call this method without any values (e.g. + /// Run() + /// ). + /// + /// + /// The string values that you specify are paired with names and stored as name-value pairs. If you specify a single string value, + /// then Arg0 will be the name assigned to the value. The value can be used in the task action where the $(Arg0) variable is used in + /// the action properties. + /// + /// + /// If you pass in values such as "0", "100", and "250" as an array of string values, then "0" will replace the $(Arg0) variables, + /// "100" will replace the $(Arg1) variables, and "250" will replace the $(Arg2) variables used in the action properties. + /// + /// + /// For more information and a list of action properties that can use $(Arg0), $(Arg1), ..., $(Arg32) variables in their values, see + ///
Task Actions. + /// + /// + /// A instance that defines the new instance of the task. + /// + /// + /// + /// + /// + public RunningTask Run(params string[] parameters) + { + if (v2Task != null) + { + if (parameters.Length > 32) + throw new ArgumentOutOfRangeException(nameof(parameters), "A maximum of 32 values is allowed."); + if (TaskService.HighestSupportedVersion < TaskServiceVersion.V1_5 && parameters.Any(p => (p?.Length ?? 0) >= 260)) + throw new ArgumentOutOfRangeException(nameof(parameters), "On systems prior to Windows 10, all individual parameters must be less than 260 characters."); + var irt = v2Task.Run(parameters.Length == 0 ? null : parameters); + return irt != null ? new RunningTask(TaskService, v2Task, irt) : null; + } + + v1Task.Run(); + return new RunningTask(TaskService, v1Task); + } + + /// Runs the registered task immediately using specified flags and a session identifier. + /// Defines how the task is run. + /// + /// The terminal server session in which you want to start the task. + /// + /// If the value is not passed into the parameter, then the value + /// specified in this parameter is ignored.If the value is passed into the flags parameter + /// and the sessionID value is less than or equal to 0, then an invalid argument error will be returned. + /// + /// + /// If the value is passed into the parameter and the sessionID + /// value is a valid session ID greater than 0 and if no value is specified for the user parameter, then the Task Scheduler service + /// will try to start the task interactively as the user who is logged on to the specified session. + /// + /// + /// If the value is passed into the parameter and the sessionID + /// value is a valid session ID greater than 0 and if a user is specified in the user parameter, then the Task Scheduler service + /// will try to start the task interactively as the user who is specified in the user parameter. + /// + /// + /// The user for which the task runs. + /// + /// + /// The parameters used as values in the task actions. A maximum of 32 parameters can be supplied. To run a task with no parameters, + /// call this method without any values (e.g. + /// RunEx(0, 0, "MyUserName") + /// ). + /// + /// + /// The string values that you specify are paired with names and stored as name-value pairs. If you specify a single string value, + /// then Arg0 will be the name assigned to the value. The value can be used in the task action where the $(Arg0) variable is used in + /// the action properties. + /// + /// + /// If you pass in values such as "0", "100", and "250" as an array of string values, then "0" will replace the $(Arg0) variables, + /// "100" will replace the $(Arg1) variables, and "250" will replace the $(Arg2) variables used in the action properties. + /// + /// + /// For more information and a list of action properties that can use $(Arg0), $(Arg1), ..., $(Arg32) variables in their values, see + /// Task Actions. + /// + /// + /// A instance that defines the new instance of the task. + /// + /// + /// This method will return without error, but the task will not run if the AllowDemandStart property of ITaskSettings is set to + /// false for the task. + /// + /// If RunEx is invoked from a disabled task, it will return null and the task will not be run. + /// + /// Not supported under Task Scheduler 1.0. + /// + /// + /// + /// + /// + public RunningTask RunEx(TaskRunFlags flags, int sessionID, string user, params string[] parameters) + { + if (v2Task == null) throw new NotV1SupportedException(); + if (parameters == null || parameters.Any(s => s == null)) + throw new ArgumentNullException(nameof(parameters), "The array and none of the values passed as parameters may be `null`."); + if (parameters.Length > 32) + throw new ArgumentOutOfRangeException(nameof(parameters), "A maximum of 32 parameters can be supplied to RunEx."); + if (TaskService.HighestSupportedVersion < TaskServiceVersion.V1_5 && parameters.Any(p => (p?.Length ?? 0) >= 260)) + throw new ArgumentOutOfRangeException(nameof(parameters), "On systems prior to Windows 10, no individual parameter may be more than 260 characters."); + var irt = v2Task.RunEx(parameters.Length == 0 ? null : parameters, (int)flags, sessionID, user); + return irt != null ? new RunningTask(TaskService, v2Task, irt) : null; + } + + /// + /// Applies access control list (ACL) entries described by a object to the file described by the current + /// object. + /// + /// + /// A object that describes an access control list (ACL) entry to apply to the current task. + /// + /// + /// Give read access to all authenticated users for a task. + /// + /// + /// + /// + public void SetAccessControl([NotNull] TaskSecurity taskSecurity) => taskSecurity.Persist(this); + + /// Sets the security descriptor for the task. Not available to Task Scheduler 1.0. + /// The security descriptor for the task. + /// Flags that specify how to set the security descriptor. + /// Not supported under Task Scheduler 1.0. + public void SetSecurityDescriptorSddlForm([NotNull] string sddlForm, TaskSetSecurityOptions options = TaskSetSecurityOptions.None) + { + if (v2Task != null) + v2Task.SetSecurityDescriptor(sddlForm, (int)options); + else + throw new NotV1SupportedException(); + } + + /// Dynamically tries to load the assembly for the editor and displays it as editable for this task. + /// true if editor returns with OK response; false otherwise. + /// + /// The Microsoft.Win32.TaskSchedulerEditor.dll assembly must reside in the same directory as the Microsoft.Win32.TaskScheduler.dll + /// or in the GAC. + /// + public bool ShowEditor() + { + try + { + var t = ReflectionHelper.LoadType("Microsoft.Win32.TaskScheduler.TaskEditDialog", "Microsoft.Win32.TaskSchedulerEditor.dll"); + if (t != null) + return ReflectionHelper.InvokeMethod(t, new object[] { this, true, true }, "ShowDialog") == 1; + } + catch { } + return false; + } + + /// Shows the property page for the task (v1.0 only). + public void ShowPropertyPage() + { + if (v1Task != null) + v1Task.EditWorkItem(IntPtr.Zero, 0); + else + throw new NotV2SupportedException(); + } + + /// Stops the registered task immediately. + /// + /// The Stop method stops all instances of the task. + /// + /// System account users can stop a task, users with Administrator group privileges can stop a task, and if a user has rights to + /// execute and read a task, then the user can stop the task. A user can stop the task instances that are running under the same + /// credentials as the user account. In all other cases, the user is denied access to stop the task. + /// + /// + public void Stop() + { + if (v2Task != null) + v2Task.Stop(0); + else + v1Task.Terminate(); + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() => Name; + + int IComparable.CompareTo(object other) => CompareTo(other as Task); + + internal static Task CreateTask(TaskService svc, IRegisteredTask iTask, bool throwError = false) + { + var iDef = GetV2Definition(svc, iTask, throwError); + if (iDef != null || !svc.AllowReadOnlyTasks) return new Task(svc, iTask, iDef); + iDef = GetV2StrippedDefinition(svc, iTask); + return new Task(svc, iTask, iDef) { ReadOnly = true }; + } + + internal static int GetOSLibraryMinorVersion() => TaskService.LibraryVersion.Minor; + + [NotNull] + internal static string GetV1Path(ITask v1Task) + { + var iFile = (IPersistFile)v1Task; + iFile.GetCurFile(out var fileName); + return fileName ?? string.Empty; + } + + /// + /// Gets the ITaskDefinition for a V2 task and prevents the errors that come when connecting remotely to a higher version of the + /// Task Scheduler. + /// + /// The local task service. + /// The task instance. + /// if set to true this method will throw an exception if unable to get the task definition. + /// A valid ITaskDefinition that should not throw errors on the local instance. + /// Unable to get a compatible task definition for this version of the library. + internal static ITaskDefinition GetV2Definition(TaskService svc, IRegisteredTask iTask, bool throwError = false) + { + var xmlVer = new Version(); + try + { + var dd = new DefDoc(iTask.Xml); + xmlVer = dd.Version; + if (xmlVer.Minor > osLibMinorVer) + { + var newMinor = xmlVer.Minor; + if (!dd.Contains("Volatile", "false", true) && + !dd.Contains("MaintenanceSettings")) + newMinor = 3; + if (!dd.Contains("UseUnifiedSchedulingEngine", "false", true) && + !dd.Contains("DisallowStartOnRemoteAppSession", "false", true) && + !dd.Contains("RequiredPrivileges") && + !dd.Contains("ProcessTokenSidType", "Default", true)) + newMinor = 2; + if (!dd.Contains("DisplayName") && + !dd.Contains("GroupId") && + !dd.Contains("RunLevel", "LeastPrivilege", true) && + !dd.Contains("SecurityDescriptor") && + !dd.Contains("Source") && + !dd.Contains("URI") && + !dd.Contains("AllowStartOnDemand", "true", true) && + !dd.Contains("AllowHardTerminate", "true", true) && + !dd.Contains("MultipleInstancesPolicy", "IgnoreNew", true) && + !dd.Contains("NetworkSettings") && + !dd.Contains("StartWhenAvailable", "false", true) && + !dd.Contains("SendEmail") && + !dd.Contains("ShowMessage") && + !dd.Contains("ComHandler") && + !dd.Contains("EventTrigger") && + !dd.Contains("SessionStateChangeTrigger") && + !dd.Contains("RegistrationTrigger") && + !dd.Contains("RestartOnFailure") && + !dd.Contains("LogonType", "None", true)) + newMinor = 1; + + if (newMinor > osLibMinorVer && throwError) + throw new InvalidOperationException($"The current version of the native library (1.{osLibMinorVer}) does not support the original or minimum version of the \"{iTask.Name}\" task ({xmlVer}/1.{newMinor}). This is likely due to attempting to read the remote tasks of a newer version of Windows from a down-level client."); + + if (newMinor != xmlVer.Minor) + { + dd.Version = new Version(1, newMinor); + var def = svc.v2TaskService.NewTask(0); + def.XmlText = dd.Xml; + return def; + } + } + return iTask.Definition; + } + catch (COMException comEx) + { + if (throwError) + { + if ((uint)comEx.ErrorCode == 0x80041318 && xmlVer.Minor != osLibMinorVer) // Incorrect XML value + throw new InvalidOperationException($"The current version of the native library (1.{osLibMinorVer}) does not support the version of the \"{iTask.Name}\" task ({xmlVer})"); + throw; + } + } + catch + { + if (throwError) + throw; + } + return null; + } + + internal static ITaskDefinition GetV2StrippedDefinition(TaskService svc, IRegisteredTask iTask) + { + try + { + var dd = new DefDoc(iTask.Xml); + var xmlVer = dd.Version; + if (xmlVer.Minor > osLibMinorVer) + { + if (osLibMinorVer < 4) + { + dd.RemoveTag("Volatile"); + dd.RemoveTag("MaintenanceSettings"); + } + if (osLibMinorVer < 3) + { + dd.RemoveTag("UseUnifiedSchedulingEngine"); + dd.RemoveTag("DisallowStartOnRemoteAppSession"); + dd.RemoveTag("RequiredPrivileges"); + dd.RemoveTag("ProcessTokenSidType"); + } + if (osLibMinorVer < 2) + { + dd.RemoveTag("DisplayName"); + dd.RemoveTag("GroupId"); + dd.RemoveTag("RunLevel"); + dd.RemoveTag("SecurityDescriptor"); + dd.RemoveTag("Source"); + dd.RemoveTag("URI"); + dd.RemoveTag("AllowStartOnDemand"); + dd.RemoveTag("AllowHardTerminate"); + dd.RemoveTag("MultipleInstancesPolicy"); + dd.RemoveTag("NetworkSettings"); + dd.RemoveTag("StartWhenAvailable"); + dd.RemoveTag("SendEmail"); + dd.RemoveTag("ShowMessage"); + dd.RemoveTag("ComHandler"); + dd.RemoveTag("EventTrigger"); + dd.RemoveTag("SessionStateChangeTrigger"); + dd.RemoveTag("RegistrationTrigger"); + dd.RemoveTag("RestartOnFailure"); + dd.RemoveTag("LogonType"); + } + dd.RemoveTag("WnfStateChangeTrigger"); // Remove custom triggers that can't be sent to Xml + dd.Version = new Version(1, osLibMinorVer); + var def = svc.v2TaskService.NewTask(0); +#if DEBUG + var path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), + $"TS_Stripped_Def_{xmlVer.Minor}-{osLibMinorVer}_{iTask.Name}.xml"); + File.WriteAllText(path, dd.Xml, Encoding.Unicode); +#endif + def.XmlText = dd.Xml; + return def; + } + } + catch (Exception ex) + { + Debug.WriteLine($"Error in GetV2StrippedDefinition: {ex}"); +#if DEBUG + throw; +#endif + } + return iTask.Definition; + } + + internal static TimeSpan StringToTimeSpan(string input) + { + if (!string.IsNullOrEmpty(input)) + try { return XmlConvert.ToTimeSpan(input); } catch { } + return TimeSpan.Zero; + } + + internal static string TimeSpanToString(TimeSpan span) + { + if (span != TimeSpan.Zero) + try { return XmlConvert.ToString(span); } catch { } + return null; + } + + internal void V1Reactivate() + { + var iTask = TaskService.GetTask(TaskService.v1TaskScheduler, Name); + if (iTask != null) + v1Task = iTask; + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + protected void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private class DefDoc + { + private readonly XmlDocument doc; + + public DefDoc(string xml) + { + doc = new XmlDocument(); + doc.LoadXml(xml); + } + + public Version Version + { + get + { + try + { + return new Version(doc["Task"].Attributes["version"].Value); + } + catch + { + throw new InvalidOperationException("Task definition does not contain a version."); + } + } + set + { + var task = doc["Task"]; + if (task != null) task.Attributes["version"].Value = value.ToString(2); + } + } + + public string Xml => doc.OuterXml; + + public bool Contains(string tag, string defaultVal = null, bool removeIfFound = false) + { + var nl = doc.GetElementsByTagName(tag); + while (nl.Count > 0) + { + var e = nl[0]; + if (e.InnerText != defaultVal || !removeIfFound || e.ParentNode == null) + return true; + e.ParentNode?.RemoveChild(e); + nl = doc.GetElementsByTagName(tag); + } + return false; + } + + public void RemoveTag(string tag) + { + var nl = doc.GetElementsByTagName(tag); + while (nl.Count > 0) + { + var e = nl[0]; + e.ParentNode?.RemoveChild(e); + nl = doc.GetElementsByTagName(tag); + } + } + } + } + + /// Contains information about the compatibility of the current configuration with a specified version. + [PublicAPI] + public class TaskCompatibilityEntry + { + internal TaskCompatibilityEntry(TaskCompatibility comp, string prop, string reason) + { + CompatibilityLevel = comp; + Property = prop; + Reason = reason; + } + + /// Gets the compatibility level. + /// The compatibility level. + public TaskCompatibility CompatibilityLevel { get; } + + /// Gets the property name with the incompatibility. + /// The property name. + public string Property { get; } + + /// Gets the reason for the incompatibility. + /// The reason. + public string Reason { get; } + } + + /// Defines all the components of a task, such as the task settings, triggers, actions, and registration information. + [XmlRoot("Task", Namespace = tns, IsNullable = false)] + [XmlSchemaProvider("GetV1SchemaFile")] + [PublicAPI, Serializable] + public sealed class TaskDefinition : IDisposable, IXmlSerializable, INotifyPropertyChanged + { + internal const string tns = "http://schemas.microsoft.com/windows/2004/02/mit/task"; + + internal string v1Name = string.Empty; + internal ITask v1Task; + internal ITaskDefinition v2Def; + + private ActionCollection actions; + private TaskPrincipal principal; + private TaskRegistrationInfo regInfo; + private TaskSettings settings; + private TriggerCollection triggers; + + internal TaskDefinition([NotNull] ITask iTask, string name) + { + v1Task = iTask; + v1Name = name; + } + + internal TaskDefinition([NotNull] ITaskDefinition iDef) => v2Def = iDef; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets a collection of actions that are performed by the task. + [XmlArrayItem(ElementName = "Exec", IsNullable = true, Type = typeof(Action.ExecAction))] + [XmlArrayItem(ElementName = "ShowMessage", IsNullable = true, Type = typeof(Action.ShowMessageAction))] + [XmlArrayItem(ElementName = "ComHandler", IsNullable = true, Type = typeof(Action.ComHandlerAction))] + [XmlArrayItem(ElementName = "SendEmail", IsNullable = true, Type = typeof(Action.EmailAction))] + [XmlArray] + [NotNull, ItemNotNull] + public ActionCollection Actions => actions ??= v2Def != null ? new ActionCollection(v2Def) : new ActionCollection(v1Task); + + /// + /// Gets or sets the data that is associated with the task. This data is ignored by the Task Scheduler service, but is used by + /// third-parties who wish to extend the task format. + /// + /// + /// For V1 tasks, this library makes special use of the SetWorkItemData and GetWorkItemData methods and does not expose that data + /// stream directly. Instead, it uses that data stream to hold a dictionary of properties that are not supported under V1, but can + /// have values under V2. An example of this is the value which is stored in the data stream. + /// + /// The library does not provide direct access to the V1 work item data. If using V2 properties with a V1 task, programmatic access + /// to the task using the native API will retrieve unreadable results from GetWorkItemData and will eliminate those property values + /// if SetWorkItemData is used. + /// + /// + [CanBeNull] + public string Data + { + get => v2Def != null ? v2Def.Data : v1Task.GetDataItem(nameof(Data)); + set + { + if (v2Def != null) + v2Def.Data = value; + else + v1Task.SetDataItem(nameof(Data), value); + OnNotifyPropertyChanged(); + } + } + + /// Gets the lowest supported version that supports the settings for this . + [XmlIgnore] + public TaskCompatibility LowestSupportedVersion => GetLowestSupportedVersion(); + + /// Gets a collection of triggers that are used to start a task. + [XmlArrayItem(ElementName = "BootTrigger", IsNullable = true, Type = typeof(BootTrigger))] + [XmlArrayItem(ElementName = "CalendarTrigger", IsNullable = true, Type = typeof(CalendarTrigger))] + [XmlArrayItem(ElementName = "IdleTrigger", IsNullable = true, Type = typeof(IdleTrigger))] + [XmlArrayItem(ElementName = "LogonTrigger", IsNullable = true, Type = typeof(LogonTrigger))] + [XmlArrayItem(ElementName = "TimeTrigger", IsNullable = true, Type = typeof(TimeTrigger))] + [XmlArray] + [NotNull, ItemNotNull] + public TriggerCollection Triggers => triggers ??= v2Def != null ? new TriggerCollection(v2Def) : new TriggerCollection(v1Task); + + /// Gets or sets the XML-formatted definition of the task. + [XmlIgnore] + public string XmlText + { + get => v2Def != null ? v2Def.XmlText : XmlSerializationHelper.WriteObjectToXmlText(this); + set + { + if (v2Def != null) + v2Def.XmlText = value; + else + XmlSerializationHelper.ReadObjectFromXmlText(value, this); + OnNotifyPropertyChanged(); + } + } + + /// Gets the principal for the task that provides the security credentials for the task. + [NotNull] + public TaskPrincipal Principal => principal ??= v2Def != null ? new TaskPrincipal(v2Def.Principal, () => XmlText) : new TaskPrincipal(v1Task); + + /// + /// Gets a class instance of registration information that is used to describe a task, such as the description of the task, the + /// author of the task, and the date the task is registered. + /// + public TaskRegistrationInfo RegistrationInfo => regInfo ??= v2Def != null ? new TaskRegistrationInfo(v2Def.RegistrationInfo) : new TaskRegistrationInfo(v1Task); + + /// Gets the settings that define how the Task Scheduler service performs the task. + [NotNull] + public TaskSettings Settings => settings ??= v2Def != null ? new TaskSettings(v2Def.Settings) : new TaskSettings(v1Task); + + /// Gets the XML Schema file for V1 tasks. + /// The for V1 tasks. + /// An object containing the XML Schema for V1 tasks. + public static XmlSchemaComplexType GetV1SchemaFile([NotNull] XmlSchemaSet xs) + { + XmlSchema schema; + using (var xsdFile = Assembly.GetAssembly(typeof(TaskDefinition)).GetManifestResourceStream("Microsoft.Win32.TaskScheduler.V1.TaskSchedulerV1Schema.xsd")) + { + var schemaSerializer = new XmlSerializer(typeof(XmlSchema)); + schema = (XmlSchema)schemaSerializer.Deserialize(XmlReader.Create(xsdFile)); + xs.Add(schema); + } + + // target namespace + var name = new XmlQualifiedName("taskType", tns); + var productType = (XmlSchemaComplexType)schema.SchemaTypes[name]; + + return productType; + } + + /// + /// Determines whether this can use the Unified Scheduling Engine or if it contains unsupported properties. + /// + /// + /// if set to true throws an with details about unsupported properties in the Data + /// property of the exception. + /// + /// + /// true if this can use the Unified Scheduling Engine; otherwise, false. + public bool CanUseUnifiedSchedulingEngine(bool throwExceptionWithDetails = false, Version taskSchedulerVersion = null) + { + var tsVer = taskSchedulerVersion ?? TaskService.LibraryVersion; + if (tsVer < TaskServiceVersion.V1_3) return false; + var ex = new InvalidOperationException { HelpLink = "http://msdn.microsoft.com/en-us/library/windows/desktop/aa384138(v=vs.85).aspx" }; + var bad = false; + /*if (Principal.LogonType == TaskLogonType.InteractiveTokenOrPassword) { - if (v2Settings != null) - v2Settings.StopOnIdleEnd = value; - else - v1Task.SetFlags(TaskFlags.KillOnIdleEnd, value); - OnNotifyPropertyChanged(); + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, "Principal.LogonType", "== TaskLogonType.InteractiveTokenOrPassword"); } - } - - /// - /// Gets or sets a value that indicates the amount of time that the Task Scheduler will wait for an idle condition to occur. If no - /// value is specified for this property, then the Task Scheduler service will wait indefinitely for an idle condition to occur. - /// - /// - /// A value that indicates the amount of time that the Task Scheduler will wait for an idle condition to occur. The minimum time - /// allowed is 1 minute. If this value is TimeSpan.Zero, then the delay will be set to the default of 1 hour. - /// - [DefaultValue(typeof(TimeSpan), "01:00:00")] - public TimeSpan WaitTimeout - { - get + if (Settings.MultipleInstances == TaskInstancesPolicy.StopExisting) { - if (v2Settings != null) - return Task.StringToTimeSpan(v2Settings.WaitTimeout); - v1Task.GetIdleWait(out var idleMin, out var _); - return TimeSpan.FromMinutes(idleMin); - } - set + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, "Settings.MultipleInstances", "== TaskInstancesPolicy.StopExisting"); + }*/ + if (Settings.NetworkSettings.Id != Guid.Empty && tsVer >= TaskServiceVersion.V1_5) + { + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, "Settings.NetworkSettings.Id", "!= Guid.Empty"); + } + /*if (!Settings.AllowHardTerminate) { - if (v2Settings != null) - { - if (value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) - throw new ArgumentOutOfRangeException(nameof(WaitTimeout)); - v2Settings.WaitTimeout = Task.TimeSpanToString(value); - } - else - { - v1Task.SetIdleWait((ushort)value.TotalMinutes, (ushort)IdleDuration.TotalMinutes); - } - OnNotifyPropertyChanged(); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Settings != null) - Marshal.ReleaseComObject(v2Settings); - v1Task = null; - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() - { - if (v2Settings != null || v1Task != null) - return DebugHelper.GetDebugString(this); - return base.ToString(); - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - /// Specifies the task settings the Task scheduler will use to start task during Automatic maintenance. - [XmlType(IncludeInSchema = false)] - [PublicAPI] - public sealed class MaintenanceSettings : IDisposable, INotifyPropertyChanged - { - private readonly ITaskSettings3 iSettings; - private IMaintenanceSettings iMaintSettings; - - internal MaintenanceSettings([CanBeNull] ITaskSettings3 iSettings3) - { - iSettings = iSettings3; - if (iSettings3 != null) - iMaintSettings = iSettings.MaintenanceSettings; - } - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, "Settings.AllowHardTerminate", "== false"); + }*/ + if (!Actions.PowerShellConversion.IsFlagSet(PowerShellActionPlatformOption.Version2)) + for (var i = 0; i < Actions.Count; i++) + { + var a = Actions[i]; + switch (a) + { + case Action.EmailAction _: + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Actions[{i}]", "== typeof(EmailAction)"); + break; + + case Action.ShowMessageAction _: + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Actions[{i}]", "== typeof(ShowMessageAction)"); + break; + } + } + if (tsVer == TaskServiceVersion.V1_3) + for (var i = 0; i < Triggers.Count; i++) + { + Trigger t; + try { t = Triggers[i]; } + catch + { + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Triggers[{i}]", "is irretrievable."); + continue; + } + switch (t) + { + case MonthlyTrigger _: + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Triggers[{i}]", "== typeof(MonthlyTrigger)"); + break; + + case MonthlyDOWTrigger _: + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Triggers[{i}]", "== typeof(MonthlyDOWTrigger)"); + break; + /*case ICalendarTrigger _ when t.Repetition.IsSet(): + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Triggers[{i}].Repetition", ""); + break; - /// - /// Gets or sets the amount of time after which the Task scheduler attempts to run the task during emergency Automatic maintenance, - /// if the task failed to complete during regular Automatic maintenance. The minimum value is one day. The value of the property should be greater than the value of the property. If the deadline is not - /// specified the task will not be started during emergency Automatic maintenance. - /// - /// Property set for a task on a Task Scheduler version prior to 2.2. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - public TimeSpan Deadline - { - get => iMaintSettings != null ? Task.StringToTimeSpan(iMaintSettings.Deadline) : TimeSpan.Zero; - set + case EventTrigger _ when ((EventTrigger)t).ValueQueries.Count > 0: + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Triggers[{i}].ValueQueries.Count", "!= 0"); + break;*/ + } + if (t.ExecutionTimeLimit != TimeSpan.Zero) + { + bad = true; + if (!throwExceptionWithDetails) return false; + TryAdd(ex.Data, $"Triggers[{i}].ExecutionTimeLimit", "!= TimeSpan.Zero"); + } + } + if (bad && throwExceptionWithDetails) + throw ex; + return !bad; + } + + /// Releases all resources used by this class. + public void Dispose() + { + regInfo = null; + triggers = null; + settings = null; + principal = null; + actions = null; + if (v2Def != null) Marshal.ReleaseComObject(v2Def); + v1Task = null; + } + + /// Validates the current . + /// + /// if set to true throw a with details about invalid properties. + /// + /// true if current is valid; false if not. + public bool Validate(bool throwException = false) + { + var ex = new InvalidOperationException(); + if (Settings.UseUnifiedSchedulingEngine) + { + try { CanUseUnifiedSchedulingEngine(throwException); } + catch (InvalidOperationException iox) + { + foreach (DictionaryEntry kvp in iox.Data) + TryAdd(ex.Data, (kvp.Key as ICloneable)?.Clone() ?? kvp.Key, (kvp.Value as ICloneable)?.Clone() ?? kvp.Value); + } + } + + if (Settings.Compatibility >= TaskCompatibility.V2_2) + { + var PT1D = TimeSpan.FromDays(1); + if (Settings.MaintenanceSettings.IsSet() && (Settings.MaintenanceSettings.Period < PT1D || Settings.MaintenanceSettings.Deadline < PT1D || Settings.MaintenanceSettings.Deadline <= Settings.MaintenanceSettings.Period)) + TryAdd(ex.Data, "Settings.MaintenanceSettings", "Period or Deadline must be at least 1 day and Deadline must be greater than Period."); + } + + var list = new List(); + if (GetLowestSupportedVersion(list) > Settings.Compatibility) + foreach (var item in list) + TryAdd(ex.Data, item.Property, item.Reason); + + var startWhenAvailable = Settings.StartWhenAvailable; + var delOldTask = Settings.DeleteExpiredTaskAfter != TimeSpan.Zero; + var v1 = Settings.Compatibility < TaskCompatibility.V2; + var hasEndBound = false; + for (var i = 0; i < Triggers.Count; i++) + { + Trigger trigger; + try { trigger = Triggers[i]; } + catch + { + TryAdd(ex.Data, $"Triggers[{i}]", "is irretrievable."); + continue; + } + if (startWhenAvailable && trigger.Repetition.Duration != TimeSpan.Zero && trigger.EndBoundary == DateTime.MaxValue) + TryAdd(ex.Data, "Settings.StartWhenAvailable", "== true requires time-based tasks with an end boundary or time-based tasks that are set to repeat infinitely."); + if (v1 && trigger.Repetition.Interval != TimeSpan.Zero && trigger.Repetition.Interval >= trigger.Repetition.Duration) + TryAdd(ex.Data, "Trigger.Repetition.Interval", ">= Trigger.Repetition.Duration under Task Scheduler 1.0."); + if (trigger.EndBoundary <= trigger.StartBoundary) + TryAdd(ex.Data, "Trigger.StartBoundary", ">= Trigger.EndBoundary is not allowed."); + if (delOldTask && trigger.EndBoundary != DateTime.MaxValue) + hasEndBound = true; + } + if (delOldTask && !hasEndBound) + TryAdd(ex.Data, "Settings.DeleteExpiredTaskAfter", "!= TimeSpan.Zero requires at least one trigger with an end boundary."); + + if (throwException && ex.Data.Count > 0) + throw ex; + return ex.Data.Count == 0; + } + + XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(XmlReader reader) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), tns); + XmlSerializationHelper.ReadObjectProperties(reader, this); + reader.ReadEndElement(); + } + + void IXmlSerializable.WriteXml(XmlWriter writer) => + // TODO:FIX writer.WriteAttributeString("version", "1.1"); + XmlSerializationHelper.WriteObjectProperties(writer, this); + + internal static Dictionary GetV1TaskDataDictionary(ITask v1Task) + { + Dictionary dict; + var o = GetV1TaskData(v1Task); + if (o is string) + dict = new Dictionary(2) { { "Data", o.ToString() }, { "Documentation", o.ToString() } }; + else + dict = o as Dictionary; + return dict ?? new Dictionary(); + } + + internal static void SetV1TaskData(ITask v1Task, object value) + { + if (value == null) + v1Task.SetWorkItemData(0, null); + else + { + var b = new BinaryFormatter(); + var stream = new MemoryStream(); + b.Serialize(stream, value); + v1Task.SetWorkItemData((ushort)stream.Length, stream.ToArray()); + } + } + + internal void V1Save(string newName) + { + if (v1Task != null) + { + Triggers.Bind(); + + var iFile = (IPersistFile)v1Task; + if (string.IsNullOrEmpty(newName) || newName == v1Name) + { + try + { + iFile.Save(null, false); + iFile = null; + return; + } + catch { } + } + + iFile.GetCurFile(out var path); + File.Delete(path); + path = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + newName + Path.GetExtension(path); + File.Delete(path); + iFile.Save(path, true); + } + } + + private static object GetV1TaskData(ITask v1Task) + { + var Data = IntPtr.Zero; + try + { + v1Task.GetWorkItemData(out var DataLen, out Data); + if (DataLen == 0) + return null; + var bytes = new byte[DataLen]; + Marshal.Copy(Data, bytes, 0, DataLen); + var stream = new MemoryStream(bytes, false); + var b = new BinaryFormatter(); + return b.Deserialize(stream); + } + catch { } + finally + { + if (Data != IntPtr.Zero) + Marshal.FreeCoTaskMem(Data); + } + return null; + } + + private static void TryAdd(IDictionary d, object k, object v) + { + if (!d.Contains(k)) + d.Add(k, v); + } + + /// Gets the lowest supported version. + /// The output list. + /// + private TaskCompatibility GetLowestSupportedVersion(IList outputList = null) + { + var res = TaskCompatibility.V1; + var list = new List(); + + //if (Principal.DisplayName != null) + // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.DisplayName", "cannot have a value.")); } + if (Principal.GroupId != null) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.GroupId", "cannot have a value.")); } + //this.Principal.Id != null || + if (Principal.LogonType == TaskLogonType.Group || Principal.LogonType == TaskLogonType.None || Principal.LogonType == TaskLogonType.S4U) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.LogonType", "cannot be Group, None or S4U.")); } + if (Principal.RunLevel == TaskRunLevel.Highest) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.RunLevel", "cannot be set to Highest.")); } + if (RegistrationInfo.SecurityDescriptorSddlForm != null) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.SecurityDescriptorSddlForm", "cannot have a value.")); } + //if (RegistrationInfo.Source != null) + // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.Source", "cannot have a value.")); } + //if (RegistrationInfo.URI != null) + // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.URI", "cannot have a value.")); } + //if (RegistrationInfo.Version != new Version(1, 0)) + // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.Version", "cannot be set or equal 1.0.")); } + if (Settings.AllowDemandStart == false) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.AllowDemandStart", "must be true.")); } + if (Settings.AllowHardTerminate == false) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.AllowHardTerminate", "must be true.")); } + if (Settings.MultipleInstances != TaskInstancesPolicy.IgnoreNew) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.MultipleInstances", "must be set to IgnoreNew.")); } + if (Settings.NetworkSettings.IsSet()) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.NetworkSetting", "cannot have a value.")); } + if (Settings.RestartCount != 0) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.RestartCount", "must be 0.")); } + if (Settings.RestartInterval != TimeSpan.Zero) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.RestartInterval", "must be 0 (TimeSpan.Zero).")); } + if (Settings.StartWhenAvailable) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.StartWhenAvailable", "must be false.")); } + + if ((Actions.PowerShellConversion & PowerShellActionPlatformOption.Version1) != PowerShellActionPlatformOption.Version1 && (Actions.ContainsType(typeof(Action.EmailAction)) || Actions.ContainsType(typeof(Action.ShowMessageAction)) || Actions.ContainsType(typeof(Action.ComHandlerAction)))) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Actions", "may only contain ExecAction types unless Actions.PowerShellConversion includes Version1.")); } + if ((Actions.PowerShellConversion & PowerShellActionPlatformOption.Version2) != PowerShellActionPlatformOption.Version2 && (Actions.ContainsType(typeof(Action.EmailAction)) || Actions.ContainsType(typeof(Action.ShowMessageAction)))) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Actions", "may only contain ExecAction and ComHanlderAction types unless Actions.PowerShellConversion includes Version2.")); } + + try + { + if (null != Triggers.Find(t => t is ITriggerDelay && ((ITriggerDelay)t).Delay != TimeSpan.Zero)) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain delays.")); } + if (null != Triggers.Find(t => t.ExecutionTimeLimit != TimeSpan.Zero || t.Id != null)) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain an ExecutionTimeLimit or Id.")); } + if (null != Triggers.Find(t => (t as LogonTrigger)?.UserId != null)) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain a LogonTrigger with a UserId.")); } + if (null != Triggers.Find(t => t is MonthlyDOWTrigger && ((MonthlyDOWTrigger)t).RunOnLastWeekOfMonth || t is MonthlyDOWTrigger && (((MonthlyDOWTrigger)t).WeeksOfMonth & (((MonthlyDOWTrigger)t).WeeksOfMonth - 1)) != 0)) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain a MonthlyDOWTrigger with RunOnLastWeekOfMonth set or multiple WeeksOfMonth.")); } + if (null != Triggers.Find(t => t is MonthlyTrigger && ((MonthlyTrigger)t).RunOnLastDayOfMonth)) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain a MonthlyTrigger with RunOnLastDayOfMonth set.")); } + if (Triggers.ContainsType(typeof(EventTrigger)) || Triggers.ContainsType(typeof(SessionStateChangeTrigger)) || Triggers.ContainsType(typeof(RegistrationTrigger))) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain EventTrigger, SessionStateChangeTrigger, or RegistrationTrigger types.")); } + } + catch + { + list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain Custom triggers.")); + } + + if (Principal.ProcessTokenSidType != TaskProcessTokenSidType.Default) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Principal.ProcessTokenSidType", "must be Default.")); } + if (Principal.RequiredPrivileges.Count > 0) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Principal.RequiredPrivileges", "must be empty.")); } + if (Settings.DisallowStartOnRemoteAppSession) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Settings.DisallowStartOnRemoteAppSession", "must be false.")); } + if (Settings.UseUnifiedSchedulingEngine) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Settings.UseUnifiedSchedulingEngine", "must be false.")); } + + if (Settings.MaintenanceSettings.IsSet()) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_2, "this.Settings.MaintenanceSettings", "must have no values set.")); } + if (Settings.Volatile) + { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_2, " this.Settings.Volatile", "must be false.")); } + + foreach (var item in list) + { + if (res < item.CompatibilityLevel) res = item.CompatibilityLevel; + outputList?.Add(item); + } + return res; + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// + /// Provides the security credentials for a principal. These security credentials define the security context for the tasks that are + /// associated with the principal. + /// + [XmlRoot("Principals", Namespace = TaskDefinition.tns, IsNullable = true)] + [PublicAPI] + public sealed class TaskPrincipal : IDisposable, IXmlSerializable, INotifyPropertyChanged + { + private const string localSystemAcct = "SYSTEM"; + private readonly IPrincipal v2Principal; + private readonly IPrincipal2 v2Principal2; + private readonly Func xmlFunc; + private TaskPrincipalPrivileges reqPriv; + private string v1CachedAcctInfo; + private ITask v1Task; + + internal TaskPrincipal([NotNull] IPrincipal iPrincipal, Func defXml) + { + xmlFunc = defXml; + v2Principal = iPrincipal; + try { if (Environment.OSVersion.Version >= new Version(6, 1)) v2Principal2 = (IPrincipal2)v2Principal; } + catch { } + } + + internal TaskPrincipal([NotNull] ITask iTask) => v1Task = iTask; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Gets the account associated with this principal. This value is pulled from the TaskDefinition's XMLText property if set. + /// + /// The account. + [DefaultValue(null), Browsable(false)] + public string Account + { + get + { + try + { + var xml = xmlFunc?.Invoke(); + if (!string.IsNullOrEmpty(xml)) + { + var doc = new XmlDocument(); + doc.LoadXml(xml); + var pn = doc.DocumentElement?["Principals"]?["Principal"]; + if (pn != null) + { + var un = pn["UserId"] ?? pn["GroupId"]; + if (un != null) + try { return User.FromSidString(un.InnerText).Name; } + catch + { + try { return new User(un.InnerText).Name; } + catch { } + } + } + } + return new User(ToString()).Name; + } + catch + { + return null; + } + } + } + + /// Gets or sets the name of the principal that is displayed in the Task Scheduler UI. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + public string DisplayName + { + get => v2Principal != null ? v2Principal.DisplayName : v1Task.GetDataItem("PrincipalDisplayName"); + set + { + if (v2Principal != null) + v2Principal.DisplayName = value; + else + v1Task.SetDataItem("PrincipalDisplayName", value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the identifier of the user group that is required to run the tasks that are associated with the principal. Setting + /// this property to something other than a null or empty string, will set the property to NULL and will set + /// the property to TaskLogonType.Group; + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + [XmlIgnore] + public string GroupId + { + get => v2Principal?.GroupId; + set + { + if (v2Principal != null) + { + if (string.IsNullOrEmpty(value)) + value = null; + else + { + v2Principal.UserId = null; + v2Principal.LogonType = TaskLogonType.Group; + } + v2Principal.GroupId = value; + } + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the identifier of the principal. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + [XmlAttribute(AttributeName = "id", DataType = "ID")] + public string Id + { + get => v2Principal != null ? v2Principal.Id : v1Task.GetDataItem("PrincipalId"); + set + { + if (v2Principal != null) + v2Principal.Id = value; + else + v1Task.SetDataItem("PrincipalId", value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the security logon method that is required to run the tasks that are associated with the principal. + /// + /// TaskLogonType values of Group, None, or S4UNot are not supported under Task Scheduler 1.0. + /// + [DefaultValue(typeof(TaskLogonType), "None")] + public TaskLogonType LogonType + { + get + { + if (v2Principal != null) + return v2Principal.LogonType; + if (UserId == localSystemAcct) + return TaskLogonType.ServiceAccount; + if (v1Task.HasFlags(TaskFlags.RunOnlyIfLoggedOn)) + return TaskLogonType.InteractiveToken; + return TaskLogonType.InteractiveTokenOrPassword; + } + set + { + if (v2Principal != null) + v2Principal.LogonType = value; + else + { + if (value == TaskLogonType.Group || value == TaskLogonType.None || value == TaskLogonType.S4U) + throw new NotV1SupportedException(); + v1Task.SetFlags(TaskFlags.RunOnlyIfLoggedOn, value == TaskLogonType.InteractiveToken); + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the task process security identifier (SID) type. + /// One of the enumeration constants. + /// Setting this value appears to break the Task Scheduler MMC and does not output in XML. Removed to prevent problems. + /// Not supported under Task Scheduler versions prior to 2.1. + [XmlIgnore, DefaultValue(typeof(TaskProcessTokenSidType), "Default")] + public TaskProcessTokenSidType ProcessTokenSidType + { + get => v2Principal2?.ProcessTokenSidType ?? TaskProcessTokenSidType.Default; + set + { + if (v2Principal2 != null) + v2Principal2.ProcessTokenSidType = value; + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_1); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets the security credentials for a principal. These security credentials define the security context for the tasks that are + /// associated with the principal. + /// + /// Setting this value appears to break the Task Scheduler MMC and does not output in XML. Removed to prevent problems. + [XmlIgnore] + public TaskPrincipalPrivileges RequiredPrivileges => reqPriv ??= new TaskPrincipalPrivileges(v2Principal2); + + /// + /// Gets or sets the identifier that is used to specify the privilege level that is required to run the tasks that are associated + /// with the principal. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TaskRunLevel), "LUA")] + [XmlIgnore] + public TaskRunLevel RunLevel + { + get => v2Principal?.RunLevel ?? TaskRunLevel.LUA; + set + { + if (v2Principal != null) + v2Principal.RunLevel = value; + else if (value != TaskRunLevel.LUA) + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the user identifier that is required to run the tasks that are associated with the principal. Setting this property + /// to something other than a null or empty string, will set the property to NULL; + /// + [DefaultValue(null)] + public string UserId + { + get + { + if (v2Principal != null) + return v2Principal.UserId; + if (v1CachedAcctInfo == null) + { + try + { + string acct = v1Task.GetAccountInformation(); + v1CachedAcctInfo = string.IsNullOrEmpty(acct) ? localSystemAcct : acct; + } + catch { v1CachedAcctInfo = string.Empty; } + } + return v1CachedAcctInfo == string.Empty ? null : v1CachedAcctInfo; + } + set + { + if (v2Principal != null) + { + if (string.IsNullOrEmpty(value)) + value = null; + else + { + v2Principal.GroupId = null; + //if (value.Contains(@"\") && !value.Contains(@"\\")) + // value = value.Replace(@"\", @"\\"); + } + v2Principal.UserId = value; + } + else + { + if (value.Equals(localSystemAcct, StringComparison.CurrentCultureIgnoreCase)) + value = ""; + v1Task.SetAccountInformation(value, IntPtr.Zero); + v1CachedAcctInfo = null; + } + OnNotifyPropertyChanged(); + } + } + + /// Validates the supplied account against the supplied . + /// The user or group account name. + /// The SID type for the process. + /// true if supplied account can be used for the supplied SID type. + public static bool ValidateAccountForSidType(string acct, TaskProcessTokenSidType sidType) + { + string[] validUserIds = { "NETWORK SERVICE", "LOCAL SERVICE", "S-1-5-19", "S-1-5-20" }; + return sidType == TaskProcessTokenSidType.Default || Array.Find(validUserIds, id => id.Equals(acct, StringComparison.InvariantCultureIgnoreCase)) != null; + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Principal != null) + Marshal.ReleaseComObject(v2Principal); + v1Task = null; + } + + /// Gets a value indicating whether current Principal settings require a password to be provided. + /// true if settings requires a password to be provided; otherwise, false. + public bool RequiresPassword() => LogonType == TaskLogonType.InteractiveTokenOrPassword || + LogonType == TaskLogonType.Password || LogonType == TaskLogonType.S4U && UserId != null && string.Compare(UserId, WindowsIdentity.GetCurrent().Name, StringComparison.OrdinalIgnoreCase) != 0; + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() => LogonType == TaskLogonType.Group ? GroupId : UserId; + + XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(XmlReader reader) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); + if (reader.HasAttributes) + Id = reader.GetAttribute("id"); + reader.Read(); + while (reader.MoveToContent() == XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "Principal": + reader.Read(); + XmlSerializationHelper.ReadObjectProperties(reader, this); + reader.ReadEndElement(); + break; + + default: + reader.Skip(); + break; + } + } + reader.ReadEndElement(); + } + + void IXmlSerializable.WriteXml(XmlWriter writer) + { + if (string.IsNullOrEmpty(ToString()) && LogonType == TaskLogonType.None) return; + writer.WriteStartElement("Principal"); + if (!string.IsNullOrEmpty(Id)) + writer.WriteAttributeString("id", Id); + XmlSerializationHelper.WriteObjectProperties(writer, this); + writer.WriteEndElement(); + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// + /// List of security credentials for a principal under version 1.3 of the Task Scheduler. These security credentials define the security + /// context for the tasks that are associated with the principal. + /// + [PublicAPI] + public sealed class TaskPrincipalPrivileges : IList + { + private readonly IPrincipal2 v2Principal2; + + internal TaskPrincipalPrivileges(IPrincipal2 iPrincipal2 = null) => v2Principal2 = iPrincipal2; + + /// Gets the number of elements contained in the . + /// The number of elements contained in the . + public int Count => v2Principal2?.RequiredPrivilegeCount ?? 0; + + /// Gets a value indicating whether the is read-only. + /// true if the is read-only; otherwise, false. + public bool IsReadOnly => false; + + /// Gets or sets the element at the specified index. + /// The element at the specified index. + /// is not a valid index in the . + /// + /// The property is set and the is read-only. + /// + public TaskPrincipalPrivilege this[int index] + { + get + { + if (v2Principal2 != null) + return (TaskPrincipalPrivilege)Enum.Parse(typeof(TaskPrincipalPrivilege), v2Principal2[index + 1]); + throw new IndexOutOfRangeException(); + } + set => throw new NotImplementedException(); + } + + /// Adds an item to the . + /// The object to add to the . + /// The is read-only. + public void Add(TaskPrincipalPrivilege item) + { + if (v2Principal2 != null) + v2Principal2.AddRequiredPrivilege(item.ToString()); + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_1); + } + + /// Determines whether the contains a specific value. + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(TaskPrincipalPrivilege item) => IndexOf(item) != -1; + + /// Copies to. + /// The array. + /// Index of the array. + public void CopyTo(TaskPrincipalPrivilege[] array, int arrayIndex) + { + using var pEnum = GetEnumerator(); + for (var i = arrayIndex; i < array.Length; i++) + { + if (!pEnum.MoveNext()) + break; + array[i] = pEnum.Current; + } + } + + /// Returns an enumerator that iterates through the collection. + /// A that can be used to iterate through the collection. + public IEnumerator GetEnumerator() => new TaskPrincipalPrivilegesEnumerator(v2Principal2); + + /// Determines the index of a specific item in the . + /// The object to locate in the . + /// The index of if found in the list; otherwise, -1. + public int IndexOf(TaskPrincipalPrivilege item) + { + for (var i = 0; i < Count; i++) + { + if (item == this[i]) + return i; + } + return -1; + } + + /// Removes all items from the . + /// The is read-only. + void ICollection.Clear() => throw new NotImplementedException(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// Inserts an item to the at the specified index. + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// is not a valid index in the . + /// The is read-only. + void IList.Insert(int index, TaskPrincipalPrivilege item) => throw new NotImplementedException(); + + /// Removes the first occurrence of a specific object from the . + /// The object to remove from the . + /// + /// true if was successfully removed from the ; + /// otherwise, false. This method also returns false if is not found in the original . + /// + /// The is read-only. + bool ICollection.Remove(TaskPrincipalPrivilege item) => throw new NotImplementedException(); + + /// Removes the item at the specified index. + /// The zero-based index of the item to remove. + /// is not a valid index in the . + /// The is read-only. + void IList.RemoveAt(int index) => throw new NotImplementedException(); + + /// Enumerates the privileges set for a principal under version 1.3 of the Task Scheduler. + public sealed class TaskPrincipalPrivilegesEnumerator : IEnumerator + { + private readonly IPrincipal2 v2Principal2; + private int cur; + + internal TaskPrincipalPrivilegesEnumerator(IPrincipal2 iPrincipal2 = null) + { + v2Principal2 = iPrincipal2; + Reset(); + } + + /// Gets the element in the collection at the current position of the enumerator. + /// The element in the collection at the current position of the enumerator. + public TaskPrincipalPrivilege Current { get; private set; } + + object IEnumerator.Current => Current; + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public void Dispose() { } + + /// Advances the enumerator to the next element of the collection. + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// The collection was modified after the enumerator was created. + public bool MoveNext() + { + if (v2Principal2 != null && cur < v2Principal2.RequiredPrivilegeCount) + { + cur++; + Current = (TaskPrincipalPrivilege)Enum.Parse(typeof(TaskPrincipalPrivilege), v2Principal2[cur]); + return true; + } + Current = 0; + return false; + } + + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// The collection was modified after the enumerator was created. + public void Reset() + { + cur = 0; + Current = 0; + } + } + } + + /// + /// Provides the administrative information that can be used to describe the task. This information includes details such as a + /// description of the task, the author of the task, the date the task is registered, and the security descriptor of the task. + /// + [XmlRoot("RegistrationInfo", Namespace = TaskDefinition.tns, IsNullable = true)] + [PublicAPI] + public sealed class TaskRegistrationInfo : IDisposable, IXmlSerializable, INotifyPropertyChanged + { + private readonly IRegistrationInfo v2RegInfo; + private ITask v1Task; + + internal TaskRegistrationInfo([NotNull] IRegistrationInfo iRegInfo) => v2RegInfo = iRegInfo; + + internal TaskRegistrationInfo([NotNull] ITask iTask) => v1Task = iTask; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets or sets the author of the task. + [DefaultValue(null)] + public string Author + { + get => v2RegInfo != null ? v2RegInfo.Author : v1Task.GetCreator(); + set + { + if (v2RegInfo != null) + v2RegInfo.Author = value; + else + v1Task.SetCreator(value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the date and time when the task is registered. + [DefaultValue(typeof(DateTime), "0001-01-01T00:00:00")] + public DateTime Date + { + get + { + if (v2RegInfo != null) + { + if (DateTime.TryParse(v2RegInfo.Date, Trigger.DefaultDateCulture, DateTimeStyles.AssumeLocal, out var ret)) + return ret; + } + else + { + var v1Path = Task.GetV1Path(v1Task); + if (!string.IsNullOrEmpty(v1Path) && File.Exists(v1Path)) + return File.GetLastWriteTime(v1Path); + } + return DateTime.MinValue; + } + set + { + if (v2RegInfo != null) + v2RegInfo.Date = value == DateTime.MinValue ? null : value.ToString(Trigger.V2BoundaryDateFormat, Trigger.DefaultDateCulture); + else + { + var v1Path = Task.GetV1Path(v1Task); + if (!string.IsNullOrEmpty(v1Path) && File.Exists(v1Path)) + File.SetLastWriteTime(v1Path, value); + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the description of the task. + [DefaultValue(null)] + public string Description + { + get => v2RegInfo != null ? FixCrLf(v2RegInfo.Description) : v1Task.GetComment(); + set + { + if (v2RegInfo != null) + v2RegInfo.Description = value; + else + v1Task.SetComment(value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets any additional documentation for the task. + [DefaultValue(null)] + public string Documentation + { + get => v2RegInfo != null ? FixCrLf(v2RegInfo.Documentation) : v1Task.GetDataItem(nameof(Documentation)); + set + { + if (v2RegInfo != null) + v2RegInfo.Documentation = value; + else + v1Task.SetDataItem(nameof(Documentation), value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the security descriptor of the task. + /// The security descriptor. + [XmlIgnore] + public GenericSecurityDescriptor SecurityDescriptor + { + get => new RawSecurityDescriptor(SecurityDescriptorSddlForm); + set => SecurityDescriptorSddlForm = value?.GetSddlForm(Task.defaultAccessControlSections); + } + + /// Gets or sets the security descriptor of the task. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + [XmlIgnore] + public string SecurityDescriptorSddlForm + { + get + { + object sddl = null; + if (v2RegInfo != null) + sddl = v2RegInfo.SecurityDescriptor; + return sddl?.ToString(); + } + set + { + if (v2RegInfo != null) + v2RegInfo.SecurityDescriptor = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets where the task originated from. For example, a task may originate from a component, service, application, or user. + /// + [DefaultValue(null)] + public string Source + { + get => v2RegInfo != null ? v2RegInfo.Source : v1Task.GetDataItem(nameof(Source)); + set + { + if (v2RegInfo != null) + v2RegInfo.Source = value; + else + v1Task.SetDataItem(nameof(Source), value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the URI of the task. + /// + /// Note: Breaking change in version 2.0. This property was previously of type . It was found that in + /// Windows 8, many of the native tasks use this property in a string format rather than in a URI format. + /// + [DefaultValue(null)] + public string URI + { + get + { + var uri = v2RegInfo != null ? v2RegInfo.URI : v1Task.GetDataItem(nameof(URI)); + return string.IsNullOrEmpty(uri) ? null : uri; + } + set + { + if (v2RegInfo != null) + v2RegInfo.URI = value; + else + v1Task.SetDataItem(nameof(URI), value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the version number of the task. + [DefaultValueEx(typeof(Version), "1.0")] + public Version Version + { + get + { + var sver = v2RegInfo != null ? v2RegInfo.Version : v1Task.GetDataItem(nameof(Version)); + if (sver != null) try { return new Version(sver); } catch { } + return new Version(1, 0); + } + set + { + if (v2RegInfo != null) + v2RegInfo.Version = value?.ToString(); + else + v1Task.SetDataItem(nameof(Version), value.ToString()); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets an XML-formatted version of the registration information for the task. + [XmlIgnore] + public string XmlText + { + get => v2RegInfo != null ? v2RegInfo.XmlText : XmlSerializationHelper.WriteObjectToXmlText(this); + set + { + if (v2RegInfo != null) + v2RegInfo.XmlText = value; + else + XmlSerializationHelper.ReadObjectFromXmlText(value, this); + OnNotifyPropertyChanged(); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + v1Task = null; + if (v2RegInfo != null) + Marshal.ReleaseComObject(v2RegInfo); + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() + { + if (v2RegInfo != null || v1Task != null) + return DebugHelper.GetDebugString(this); + return base.ToString(); + } + + XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(XmlReader reader) + { + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); + XmlSerializationHelper.ReadObjectProperties(reader, this, ProcessVersionXml); + reader.ReadEndElement(); + } + else + reader.Skip(); + } + + void IXmlSerializable.WriteXml(XmlWriter writer) => XmlSerializationHelper.WriteObjectProperties(writer, this, ProcessVersionXml); + + internal static string FixCrLf(string text) => text == null ? null : Regex.Replace(text, "(?Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private bool ProcessVersionXml(PropertyInfo pi, object obj, ref object value) + { + if (pi.Name != "Version" || value == null) return false; + if (value is Version) + value = value.ToString(); + else if (value is string) + value = new Version(value.ToString()); + return true; + } + } + + /// Provides the settings that the Task Scheduler service uses to perform the task. + [XmlRoot("Settings", Namespace = TaskDefinition.tns, IsNullable = true)] + [PublicAPI] + public sealed class TaskSettings : IDisposable, IXmlSerializable, INotifyPropertyChanged + { + private const uint InfiniteRunTimeV1 = 0xFFFFFFFF; + + private readonly ITaskSettings v2Settings; + private readonly ITaskSettings2 v2Settings2; + private readonly ITaskSettings3 v2Settings3; + private IdleSettings idleSettings; + private MaintenanceSettings maintenanceSettings; + private NetworkSettings networkSettings; + private ITask v1Task; + + internal TaskSettings([NotNull] ITaskSettings iSettings) + { + v2Settings = iSettings; + try { if (Environment.OSVersion.Version >= new Version(6, 1)) v2Settings2 = (ITaskSettings2)v2Settings; } + catch { } + try { if (Environment.OSVersion.Version >= new Version(6, 2)) v2Settings3 = (ITaskSettings3)v2Settings; } + catch { } + } + + internal TaskSettings([NotNull] ITask iTask) => v1Task = iTask; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Gets or sets a Boolean value that indicates that the task can be started by using either the Run command or the Context menu. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(true)] + [XmlElement("AllowStartOnDemand")] + [XmlIgnore] + public bool AllowDemandStart + { + get => v2Settings == null || v2Settings.AllowDemandStart; + set + { + if (v2Settings != null) + v2Settings.AllowDemandStart = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the task may be terminated by using TerminateProcess. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(true)] + [XmlIgnore] + public bool AllowHardTerminate + { + get => v2Settings == null || v2Settings.AllowHardTerminate; + set + { + if (v2Settings != null) + v2Settings.AllowHardTerminate = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets an integer value that indicates which version of Task Scheduler a task is compatible with. + /// Not supported under Task Scheduler 1.0. + [XmlIgnore] + public TaskCompatibility Compatibility + { + get => v2Settings?.Compatibility ?? TaskCompatibility.V1; + set + { + if (v2Settings != null) + v2Settings.Compatibility = value; + else + if (value != TaskCompatibility.V1) + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the amount of time that the Task Scheduler will wait before deleting the task after it expires. If no value is + /// specified for this property, then the Task Scheduler service will not delete the task. + /// + /// + /// Gets and sets the amount of time that the Task Scheduler will wait before deleting the task after it expires. A TimeSpan value + /// of 1 second indicates the task is set to delete when done. A value of TimeSpan.Zero indicates that the task should not be deleted. + /// + /// + /// A task expires after the end boundary has been exceeded for all triggers associated with the task. The end boundary for a + /// trigger is specified by the EndBoundary property of all trigger types. + /// + [DefaultValue(typeof(TimeSpan), "12:00:00")] + public TimeSpan DeleteExpiredTaskAfter + { + get + { + if (v2Settings != null) + return v2Settings.DeleteExpiredTaskAfter == "PT0S" ? TimeSpan.FromSeconds(1) : Task.StringToTimeSpan(v2Settings.DeleteExpiredTaskAfter); + return v1Task.HasFlags(TaskFlags.DeleteWhenDone) ? TimeSpan.FromSeconds(1) : TimeSpan.Zero; + } + set + { + if (v2Settings != null) + v2Settings.DeleteExpiredTaskAfter = value == TimeSpan.FromSeconds(1) ? "PT0S" : Task.TimeSpanToString(value); + else + v1Task.SetFlags(TaskFlags.DeleteWhenDone, value >= TimeSpan.FromSeconds(1)); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the task will not be started if the computer is running on battery power. + /// + [DefaultValue(true)] + public bool DisallowStartIfOnBatteries + { + get => v2Settings?.DisallowStartIfOnBatteries ?? v1Task.HasFlags(TaskFlags.DontStartIfOnBatteries); + set + { + if (v2Settings != null) + v2Settings.DisallowStartIfOnBatteries = value; + else + v1Task.SetFlags(TaskFlags.DontStartIfOnBatteries, value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the task will not be started if the task is triggered to run in a Remote + /// Applications Integrated Locally (RAIL) session. + /// + /// Property set for a task on a Task Scheduler version prior to 2.1. + [DefaultValue(false)] + [XmlIgnore] + public bool DisallowStartOnRemoteAppSession + { + get + { + if (v2Settings2 != null) + return v2Settings2.DisallowStartOnRemoteAppSession; + if (v2Settings3 != null) + return v2Settings3.DisallowStartOnRemoteAppSession; + return false; + } + set + { + if (v2Settings2 != null) + v2Settings2.DisallowStartOnRemoteAppSession = value; + else if (v2Settings3 != null) + v2Settings3.DisallowStartOnRemoteAppSession = value; + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_1); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the task is enabled. The task can be performed only when this setting is TRUE. + /// + [DefaultValue(true)] + public bool Enabled + { + get => v2Settings?.Enabled ?? !v1Task.HasFlags(TaskFlags.Disabled); + set + { + if (v2Settings != null) + v2Settings.Enabled = value; + else + v1Task.SetFlags(TaskFlags.Disabled, !value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the amount of time that is allowed to complete the task. By default, a task will be stopped 72 hours after it + /// starts to run. + /// + /// + /// The amount of time that is allowed to complete the task. When this parameter is set to , the + /// execution time limit is infinite. + /// + /// + /// If a task is started on demand, the ExecutionTimeLimit setting is bypassed. Therefore, a task that is started on demand will not + /// be terminated if it exceeds the ExecutionTimeLimit. + /// + [DefaultValue(typeof(TimeSpan), "3")] + public TimeSpan ExecutionTimeLimit + { + get + { + if (v2Settings != null) + return Task.StringToTimeSpan(v2Settings.ExecutionTimeLimit); + var ms = v1Task.GetMaxRunTime(); + return ms == InfiniteRunTimeV1 ? TimeSpan.Zero : TimeSpan.FromMilliseconds(ms); + } + set + { + if (v2Settings != null) + v2Settings.ExecutionTimeLimit = value == TimeSpan.Zero ? "PT0S" : Task.TimeSpanToString(value); + else + { + // Due to an issue introduced in Vista, and propagated to Windows 7, setting the MaxRunTime to INFINITE results in the + // task only running for 72 hours. For these operating systems, setting the RunTime to "INFINITE - 1" gets the desired + // behavior of allowing an "infinite" run of the task. + var ms = value == TimeSpan.Zero ? InfiniteRunTimeV1 : Convert.ToUInt32(value.TotalMilliseconds); + v1Task.SetMaxRunTime(ms); + if (value == TimeSpan.Zero && v1Task.GetMaxRunTime() != InfiniteRunTimeV1) + v1Task.SetMaxRunTime(InfiniteRunTimeV1 - 1); + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the task will not be visible in the UI by default. + [DefaultValue(false)] + public bool Hidden + { + get => v2Settings?.Hidden ?? v1Task.HasFlags(TaskFlags.Hidden); + set + { + if (v2Settings != null) + v2Settings.Hidden = value; + else + v1Task.SetFlags(TaskFlags.Hidden, value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the information that the Task Scheduler uses during Automatic maintenance. + [XmlIgnore] + [NotNull] + public MaintenanceSettings MaintenanceSettings => maintenanceSettings ??= new MaintenanceSettings(v2Settings3); + + /// Gets or sets the policy that defines how the Task Scheduler handles multiple instances of the task. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TaskInstancesPolicy), "IgnoreNew")] + [XmlIgnore] + public TaskInstancesPolicy MultipleInstances + { + get => v2Settings?.MultipleInstances ?? TaskInstancesPolicy.IgnoreNew; + set + { + if (v2Settings != null) + v2Settings.MultipleInstances = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the priority level of the task. + /// The priority. + /// Value set to AboveNormal or BelowNormal on Task Scheduler 1.0. + [DefaultValue(typeof(ProcessPriorityClass), "Normal")] + public ProcessPriorityClass Priority + { + get => v2Settings != null ? GetPriorityFromInt(v2Settings.Priority) : (ProcessPriorityClass)v1Task.GetPriority(); + set + { + if (v2Settings != null) + { + v2Settings.Priority = GetPriorityAsInt(value); + } + else + { + if (value == ProcessPriorityClass.AboveNormal || value == ProcessPriorityClass.BelowNormal) + throw new NotV1SupportedException("Unsupported priority level on Task Scheduler 1.0."); + v1Task.SetPriority((uint)value); + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the number of times that the Task Scheduler will attempt to restart the task. + /// + /// The number of times that the Task Scheduler will attempt to restart the task. If this property is set, the property must also be set. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(0)] + [XmlIgnore] + public int RestartCount + { + get => v2Settings?.RestartCount ?? 0; + set + { + if (v2Settings != null) + v2Settings.RestartCount = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a value that specifies how long the Task Scheduler will attempt to restart the task. + /// + /// A value that specifies how long the Task Scheduler will attempt to restart the task. If this property is set, the property must also be set. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan RestartInterval + { + get => v2Settings != null ? Task.StringToTimeSpan(v2Settings.RestartInterval) : TimeSpan.Zero; + set + { + if (v2Settings != null) + v2Settings.RestartInterval = Task.TimeSpanToString(value); + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the Task Scheduler will run the task only if the computer is in an idle condition. + /// + [DefaultValue(false)] + public bool RunOnlyIfIdle + { + get => v2Settings?.RunOnlyIfIdle ?? v1Task.HasFlags(TaskFlags.StartOnlyIfIdle); + set + { + if (v2Settings != null) + v2Settings.RunOnlyIfIdle = value; + else + v1Task.SetFlags(TaskFlags.StartOnlyIfIdle, value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the Task Scheduler will run the task only if the user is logged on (v1.0 only) + /// + /// Property set for a task on a Task Scheduler version other than 1.0. + [XmlIgnore] + public bool RunOnlyIfLoggedOn + { + get => v2Settings != null || v1Task.HasFlags(TaskFlags.RunOnlyIfLoggedOn); + set + { + if (v1Task != null) + v1Task.SetFlags(TaskFlags.RunOnlyIfLoggedOn, value); + else if (v2Settings != null) + throw new NotV2SupportedException("Task Scheduler 2.0 (1.2) does not support setting this property. You must use an InteractiveToken in order to have the task run in the current user session."); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the Task Scheduler will run the task only when a network is available. + [DefaultValue(false)] + public bool RunOnlyIfNetworkAvailable + { + get => v2Settings?.RunOnlyIfNetworkAvailable ?? v1Task.HasFlags(TaskFlags.RunIfConnectedToInternet); + set + { + if (v2Settings != null) + v2Settings.RunOnlyIfNetworkAvailable = value; + else + v1Task.SetFlags(TaskFlags.RunIfConnectedToInternet, value); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the Task Scheduler can start the task at any time after its scheduled time has passed. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(false)] + [XmlIgnore] + public bool StartWhenAvailable + { + get => v2Settings != null && v2Settings.StartWhenAvailable; + set + { + if (v2Settings != null) + v2Settings.StartWhenAvailable = value; + else + throw new NotV1SupportedException(); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the task will be stopped if the computer switches to battery power. + [DefaultValue(true)] + public bool StopIfGoingOnBatteries + { + get => v2Settings?.StopIfGoingOnBatteries ?? v1Task.HasFlags(TaskFlags.KillIfGoingOnBatteries); + set + { + if (v2Settings != null) + v2Settings.StopIfGoingOnBatteries = value; + else + v1Task.SetFlags(TaskFlags.KillIfGoingOnBatteries, value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the Unified Scheduling Engine will be utilized to run this task. + /// Property set for a task on a Task Scheduler version prior to 2.1. + [DefaultValue(false)] + [XmlIgnore] + public bool UseUnifiedSchedulingEngine + { + get + { + if (v2Settings2 != null) + return v2Settings2.UseUnifiedSchedulingEngine; + if (v2Settings3 != null) + return v2Settings3.UseUnifiedSchedulingEngine; + return false; + } + set + { + if (v2Settings2 != null) + v2Settings2.UseUnifiedSchedulingEngine = value; + else if (v2Settings3 != null) + v2Settings3.UseUnifiedSchedulingEngine = value; + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_1); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a boolean value that indicates whether the task is automatically disabled every time Windows starts. + /// Property set for a task on a Task Scheduler version prior to 2.2. + [DefaultValue(false)] + [XmlIgnore] + public bool Volatile + { + get => v2Settings3 != null && v2Settings3.Volatile; + set + { + if (v2Settings3 != null) + v2Settings3.Volatile = value; + else + throw new NotSupportedPriorToException(TaskCompatibility.V2_2); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates that the Task Scheduler will wake the computer when it is time to run the task. + /// + [DefaultValue(false)] + public bool WakeToRun + { + get => v2Settings?.WakeToRun ?? v1Task.HasFlags(TaskFlags.SystemRequired); + set + { + if (v2Settings != null) + v2Settings.WakeToRun = value; + else + v1Task.SetFlags(TaskFlags.SystemRequired, value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets an XML-formatted definition of the task settings. + [XmlIgnore] + public string XmlText + { + get => v2Settings != null ? v2Settings.XmlText : XmlSerializationHelper.WriteObjectToXmlText(this); + set + { + if (v2Settings != null) + v2Settings.XmlText = value; + else + XmlSerializationHelper.ReadObjectFromXmlText(value, this); + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the information that specifies how the Task Scheduler performs tasks when the computer is in an idle state. + /// + [NotNull] + public IdleSettings IdleSettings => idleSettings ??= v2Settings != null ? new IdleSettings(v2Settings.IdleSettings) : new IdleSettings(v1Task); + + /// + /// Gets or sets the network settings object that contains a network profile identifier and name. If the RunOnlyIfNetworkAvailable + /// property of ITaskSettings is true and a network profile is specified in the NetworkSettings property, then the task will run + /// only if the specified network profile is available. + /// + [XmlIgnore] + [NotNull] + public NetworkSettings NetworkSettings => networkSettings ??= new NetworkSettings(v2Settings?.NetworkSettings); + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Settings != null) + Marshal.ReleaseComObject(v2Settings); + idleSettings = null; + networkSettings = null; + v1Task = null; + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() + { + if (v2Settings != null || v1Task != null) + return DebugHelper.GetDebugString(this); + return base.ToString(); + } + + XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(XmlReader reader) + { + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); + XmlSerializationHelper.ReadObjectProperties(reader, this, ConvertXmlProperty); + reader.ReadEndElement(); + } + else + reader.Skip(); + } + + void IXmlSerializable.WriteXml(XmlWriter writer) => XmlSerializationHelper.WriteObjectProperties(writer, this, ConvertXmlProperty); + + private bool ConvertXmlProperty(PropertyInfo pi, object obj, ref object value) + { + if (pi.Name == "Priority" && value != null) + { + if (value is int) + value = GetPriorityFromInt((int)value); + else if (value is ProcessPriorityClass) + value = GetPriorityAsInt((ProcessPriorityClass)value); + return true; + } + return false; + } + + private int GetPriorityAsInt(ProcessPriorityClass value) + { + // Check for back-door case where exact value is being passed and cast to ProcessPriorityClass + if ((int)value <= 10 && value >= 0) return (int)value; + var p = 7; + switch (value) + { + case ProcessPriorityClass.AboveNormal: + p = 3; + break; + + case ProcessPriorityClass.High: + p = 1; + break; + + case ProcessPriorityClass.Idle: + p = 10; + break; + + case ProcessPriorityClass.Normal: + p = 5; + break; + + case ProcessPriorityClass.RealTime: + p = 0; + break; + // case ProcessPriorityClass.BelowNormal: default: break; + } + return p; + } + + private ProcessPriorityClass GetPriorityFromInt(int value) + { + switch (value) + { + case 0: + return ProcessPriorityClass.RealTime; + + case 1: + return ProcessPriorityClass.High; + + case 2: + case 3: + return ProcessPriorityClass.AboveNormal; + + case 4: + case 5: + case 6: + return ProcessPriorityClass.Normal; + // case 7: case 8: + default: + return ProcessPriorityClass.BelowNormal; + + case 9: + case 10: + return ProcessPriorityClass.Idle; + } + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + internal static class DebugHelper + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Language", "CSE0003:Use expression-bodied members", Justification = "")] + public static string GetDebugString(object inst) + { +#if DEBUG + var sb = new StringBuilder(); + foreach (var pi in inst.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { - if (iSettings != null) + if (pi.Name.StartsWith("Xml")) + continue; + var outval = pi.GetValue(inst, null); + if (outval != null) { - if (iMaintSettings == null && value != TimeSpan.Zero) - iMaintSettings = iSettings.CreateMaintenanceSettings(); - if (iMaintSettings != null) - iMaintSettings.Deadline = Task.TimeSpanToString(value); - } - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_2); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a value indicating whether the Task Scheduler must start the task during the Automatic maintenance in exclusive - /// mode. The exclusivity is guaranteed only between other maintenance tasks and doesn't grant any ordering priority of the task. If - /// exclusivity is not specified, the task is started in parallel with other maintenance tasks. - /// - /// Property set for a task on a Task Scheduler version prior to 2.2. - [DefaultValue(false)] - public bool Exclusive - { - get => iMaintSettings != null && iMaintSettings.Exclusive; - set - { - if (iSettings != null) - { - if (iMaintSettings == null && value) - iMaintSettings = iSettings.CreateMaintenanceSettings(); - if (iMaintSettings != null) - iMaintSettings.Exclusive = value; - } - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_2); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the amount of time the task needs to be started during Automatic maintenance. The minimum value is one minute. - /// - /// Property set for a task on a Task Scheduler version prior to 2.2. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - public TimeSpan Period - { - get => iMaintSettings != null ? Task.StringToTimeSpan(iMaintSettings.Period) : TimeSpan.Zero; - set - { - if (iSettings != null) - { - if (iMaintSettings == null && value != TimeSpan.Zero) - iMaintSettings = iSettings.CreateMaintenanceSettings(); - if (iMaintSettings != null) - iMaintSettings.Period = Task.TimeSpanToString(value); - } - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_2); - OnNotifyPropertyChanged(); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (iMaintSettings != null) - Marshal.ReleaseComObject(iMaintSettings); - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() => iMaintSettings != null ? DebugHelper.GetDebugString(this) : base.ToString(); - - internal bool IsSet() => iMaintSettings != null && (iMaintSettings.Period != null || iMaintSettings.Deadline != null || iMaintSettings.Exclusive); - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - /// Provides the settings that the Task Scheduler service uses to obtain a network profile. - [XmlType(IncludeInSchema = false)] - [PublicAPI] - public sealed class NetworkSettings : IDisposable, INotifyPropertyChanged - { - private readonly INetworkSettings v2Settings; - - internal NetworkSettings([CanBeNull] INetworkSettings iSettings) => v2Settings = iSettings; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets or sets a GUID value that identifies a network profile. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(Guid), "00000000-0000-0000-0000-000000000000")] - public Guid Id - { - get - { - string id = null; - if (v2Settings != null) - id = v2Settings.Id; - return string.IsNullOrEmpty(id) ? Guid.Empty : new Guid(id); - } - set - { - if (v2Settings != null) - v2Settings.Id = value == Guid.Empty ? null : value.ToString(); - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the name of a network profile. The name is used for display purposes. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - public string Name - { - get => v2Settings?.Name; - set - { - if (v2Settings != null) - v2Settings.Name = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Settings != null) - Marshal.ReleaseComObject(v2Settings); - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() - { - if (v2Settings != null) - return DebugHelper.GetDebugString(this); - return base.ToString(); - } - - internal bool IsSet() => v2Settings != null && (!string.IsNullOrEmpty(v2Settings.Id) || !string.IsNullOrEmpty(v2Settings.Name)); - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - /// Provides the methods to get information from and control a running task. - [XmlType(IncludeInSchema = false)] - [PublicAPI] - public sealed class RunningTask : Task - { - private readonly IRunningTask v2RunningTask; - - internal RunningTask([NotNull] TaskService svc, [NotNull] IRegisteredTask iTask, [NotNull] IRunningTask iRunningTask) - : base(svc, iTask) => v2RunningTask = iRunningTask; - - internal RunningTask([NotNull] TaskService svc, [NotNull] ITask iTask) - : base(svc, iTask) - { - } - - /// Gets the process ID for the engine (process) which is running the task. - /// Not supported under Task Scheduler 1.0. - public uint EnginePID - { - get - { - if (v2RunningTask != null) - return v2RunningTask.EnginePID; - throw new NotV1SupportedException(); - } - } - - /// Gets the name of the current action that the running task is performing. - public string CurrentAction => v2RunningTask != null ? v2RunningTask.CurrentAction : v1Task.GetApplicationName(); - - /// Gets the GUID identifier for this instance of the task. - public Guid InstanceGuid => v2RunningTask != null ? new Guid(v2RunningTask.InstanceGuid) : Guid.Empty; - - /// Gets the operational state of the running task. - public override TaskState State => v2RunningTask?.State ?? base.State; - - /// Releases all resources used by this class. - public new void Dispose() - { - base.Dispose(); - if (v2RunningTask != null) Marshal.ReleaseComObject(v2RunningTask); - } - - /// Refreshes all of the local instance variables of the task. - /// Thrown if task is no longer running. - public void Refresh() - { - try { v2RunningTask?.Refresh(); } - catch (COMException ce) when ((uint)ce.ErrorCode == 0x8004130B) - { - throw new InvalidOperationException("The current task is no longer running.", ce); - } - } - } - - /// - /// Provides the methods that are used to run the task immediately, get any running instances of the task, get or set the credentials - /// that are used to register the task, and the properties that describe the task. - /// - [XmlType(IncludeInSchema = false)] - [PublicAPI] - public class Task : IDisposable, IComparable, IComparable, INotifyPropertyChanged - { - internal const AccessControlSections defaultAccessControlSections = AccessControlSections.Owner | AccessControlSections.Group | AccessControlSections.Access; - internal const SecurityInfos defaultSecurityInfosSections = SecurityInfos.Owner | SecurityInfos.Group | SecurityInfos.DiscretionaryAcl; - internal ITask v1Task; - - private static readonly int osLibMinorVer = GetOSLibraryMinorVersion(); - private static readonly DateTime v2InvalidDate = new DateTime(1899, 12, 30); - private readonly IRegisteredTask v2Task; - private TaskDefinition myTD; - - internal Task([NotNull] TaskService svc, [NotNull] ITask iTask) - { - TaskService = svc; - v1Task = iTask; - ReadOnly = false; - } - - internal Task([NotNull] TaskService svc, [NotNull] IRegisteredTask iTask, ITaskDefinition iDef = null) - { - TaskService = svc; - v2Task = iTask; - if (iDef != null) - myTD = new TaskDefinition(iDef); - ReadOnly = false; - } - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets the definition of the task. - [NotNull] - public TaskDefinition Definition => myTD ??= v2Task != null ? new TaskDefinition(GetV2Definition(TaskService, v2Task, true)) : new TaskDefinition(v1Task, Name); - - /// Gets or sets a Boolean value that indicates if the registered task is enabled. - /// - /// As of version 1.8.1, under V1 systems (prior to Vista), this property will immediately update the Disabled state and re-save the - /// current task. If changes have been made to the , then those changes will be saved. - /// - public bool Enabled - { - get => v2Task?.Enabled ?? Definition.Settings.Enabled; - set - { - if (v2Task != null) - v2Task.Enabled = value; - else - { - Definition.Settings.Enabled = value; - Definition.V1Save(null); - } - OnNotifyPropertyChanged(); - } - } - - /// Gets an instance of the parent folder. - /// A object representing the parent folder of this task. - [NotNull] - public TaskFolder Folder - { - get - { - if (v2Task == null) - return TaskService.RootFolder; - - var path = v2Task.Path; - var parentPath = System.IO.Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(parentPath) || parentPath == TaskFolder.rootString) - return TaskService.RootFolder; - return TaskService.GetFolder(parentPath); - } - } - - /// Gets a value indicating whether this task instance is active. - /// true if this task instance is active; otherwise, false. - public bool IsActive - { - get - { - var now = DateTime.Now; - if (!Definition.Settings.Enabled) return false; - foreach (var trigger in Definition.Triggers) - { - if (!trigger.Enabled || now < trigger.StartBoundary || now > trigger.EndBoundary) continue; - if (!(trigger is ICalendarTrigger) || DateTime.MinValue != NextRunTime || trigger is TimeTrigger) - return true; - } - return false; - } - } - - /// Gets the time the registered task was last run. - /// Returns if there are no prior run times. - public DateTime LastRunTime - { - get - { - if (v2Task == null) return v1Task.GetMostRecentRunTime(); - var dt = v2Task.LastRunTime; - return dt == v2InvalidDate ? DateTime.MinValue : dt; - } - } - - /// Gets the results that were returned the last time the registered task was run. - /// The value returned is the last exit code of the last program run via an . - /// - /// - /// - /// - /// - public int LastTaskResult - { - get - { - if (v2Task != null) - return v2Task.LastTaskResult; - return (int)v1Task.GetExitCode(); - } - } - - /// Gets the time when the registered task is next scheduled to run. - /// Returns if there are no future run times. - /// - /// Potentially breaking change in release 1.8.2. For Task Scheduler 2.0, the return value prior to 1.8.2 would be Dec 30, 1899 if - /// there were no future run times. For 1.0, that value would have been DateTime.MinValue. In release 1.8.2 and later, all - /// versions will return DateTime.MinValue if there are no future run times. While this is different from the native 2.0 - /// library, it was deemed more appropriate to have consistency between the two libraries and with other .NET libraries. - /// - public DateTime NextRunTime - { - get - { - if (v2Task == null) return v1Task.GetNextRunTime(); - var ret = v2Task.NextRunTime; - if (ret != DateTime.MinValue && ret != v2InvalidDate) return ret == v2InvalidDate ? DateTime.MinValue : ret; - var nrts = GetRunTimes(DateTime.Now, DateTime.MaxValue, 1); - ret = nrts.Length > 0 ? nrts[0] : DateTime.MinValue; - return ret == v2InvalidDate ? DateTime.MinValue : ret; - } - } - - /// - /// Gets a value indicating whether this task is read only. Only available if is true. - /// - /// true if read only; otherwise, false. - public bool ReadOnly { get; internal set; } - - /// Gets or sets the security descriptor for the task. - /// The security descriptor. - [Obsolete("This property will be removed in deference to the GetAccessControl, GetSecurityDescriptorSddlForm, SetAccessControl and SetSecurityDescriptorSddlForm methods.")] - public GenericSecurityDescriptor SecurityDescriptor - { - get - { - var sddl = GetSecurityDescriptorSddlForm(); - return new RawSecurityDescriptor(sddl); - } - set => SetSecurityDescriptorSddlForm(value.GetSddlForm(defaultAccessControlSections)); - } - - /// Gets the operational state of the registered task. - public virtual TaskState State - { - get - { - if (v2Task != null) - return v2Task.State; - - V1Reactivate(); - if (!Enabled) - return TaskState.Disabled; - switch (v1Task.GetStatus()) - { - case TaskStatus.Ready: - case TaskStatus.NeverRun: - case TaskStatus.NoMoreRuns: - case TaskStatus.Terminated: - return TaskState.Ready; - - case TaskStatus.Running: - return TaskState.Running; - - case TaskStatus.Disabled: - return TaskState.Disabled; - // case TaskStatus.NotScheduled: case TaskStatus.NoTriggers: case TaskStatus.NoTriggerTime: - default: - return TaskState.Unknown; - } - } - } - - /// Gets or sets the that manages this task. - /// The task service. - public TaskService TaskService { get; } - - /// Gets the name of the registered task. - [NotNull] - public string Name => v2Task != null ? v2Task.Name : System.IO.Path.GetFileNameWithoutExtension(GetV1Path(v1Task)); - - /// Gets the number of times the registered task has missed a scheduled run. - /// Not supported under Task Scheduler 1.0. - public int NumberOfMissedRuns => v2Task?.NumberOfMissedRuns ?? throw new NotV1SupportedException(); - - /// Gets the path to where the registered task is stored. - [NotNull] - public string Path => v2Task != null ? v2Task.Path : "\\" + Name; - - /// Gets the XML-formatted registration information for the registered task. - public string Xml => v2Task != null ? v2Task.Xml : Definition.XmlText; - - /// - /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current - /// instance precedes, follows, or occurs in the same position in the sort order as the other object. - /// - /// An object to compare with this instance. - /// A value that indicates the relative order of the objects being compared. - public int CompareTo(Task other) => string.Compare(Path, other?.Path, StringComparison.InvariantCulture); - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Task != null) - Marshal.ReleaseComObject(v2Task); - v1Task = null; - } - - /// Exports the task to the specified file in XML. - /// Name of the output file. - public void Export([NotNull] string outputFileName) => File.WriteAllText(outputFileName, Xml, Encoding.Unicode); - - /// - /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task - /// described by the current object. - /// - /// A object that encapsulates the access control rules for the current task. - public TaskSecurity GetAccessControl() => GetAccessControl(defaultAccessControlSections); - - /// - /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task - /// described by the current object. - /// - /// - /// One of the values that specifies which group of access control - /// entries to retrieve. - /// - /// A object that encapsulates the access control rules for the current task. - public TaskSecurity GetAccessControl(AccessControlSections includeSections) => new TaskSecurity(this, includeSections); - - /// Gets all instances of the currently running registered task. - /// A with all instances of current task. - /// Not supported under Task Scheduler 1.0. - [NotNull, ItemNotNull] - public RunningTaskCollection GetInstances() => v2Task != null - ? new RunningTaskCollection(TaskService, v2Task.GetInstances(0)) - : throw new NotV1SupportedException(); - - /// - /// Gets the last registration time, looking first at the value and then looking for the - /// most recent registration event in the Event Log. - /// - /// of the last registration or if no value can be found. - public DateTime GetLastRegistrationTime() - { - var ret = Definition.RegistrationInfo.Date; - if (ret != DateTime.MinValue) return ret; - var log = new TaskEventLog(Path, new[] { (int)StandardTaskEventId.JobRegistered }, null, TaskService.TargetServer, TaskService.UserAccountDomain, TaskService.UserName, TaskService.UserPassword); - if (!log.Enabled) return ret; - foreach (var item in log) - { - if (item.TimeCreated.HasValue) - return item.TimeCreated.Value; - } - return ret; - } - - /// Gets the times that the registered task is scheduled to run during a specified time. - /// The starting time for the query. - /// The ending time for the query. - /// The requested number of runs. A value of 0 will return all times requested. - /// The scheduled times that the task will run. - [NotNull] - public DateTime[] GetRunTimes(DateTime start, DateTime end, uint count = 0) - { - const ushort TASK_MAX_RUN_TIMES = 1440; - - NativeMethods.SYSTEMTIME stStart = start; - NativeMethods.SYSTEMTIME stEnd = end; - var runTimes = IntPtr.Zero; - var ret = new DateTime[0]; - try - { - if (v2Task != null) - v2Task.GetRunTimes(ref stStart, ref stEnd, ref count, ref runTimes); - else - { - var count1 = count > 0 && count <= TASK_MAX_RUN_TIMES ? (ushort)count : TASK_MAX_RUN_TIMES; - v1Task.GetRunTimes(ref stStart, ref stEnd, ref count1, ref runTimes); - count = count1; - } - ret = InteropUtil.ToArray(runTimes, (int)count); - } - catch (Exception ex) - { - Debug.WriteLine($"Task.GetRunTimes failed: Error {ex}."); - } - finally - { - Marshal.FreeCoTaskMem(runTimes); - } - Debug.WriteLine($"Task.GetRunTimes ({(v2Task != null ? "V2" : "V1")}): Returned {count} items from {stStart} to {stEnd}."); - return ret; - } - - /// Gets the security descriptor for the task. Not available to Task Scheduler 1.0. - /// Section(s) of the security descriptor to return. - /// The security descriptor for the task. - /// Not supported under Task Scheduler 1.0. - public string GetSecurityDescriptorSddlForm(SecurityInfos includeSections = defaultSecurityInfosSections) => v2Task != null ? v2Task.GetSecurityDescriptor((int)includeSections) : throw new NotV1SupportedException(); - - /// - /// Updates the task with any changes made to the by calling from the currently registered folder using the currently - /// registered name. - /// - /// Thrown if task was previously registered with a password. - public void RegisterChanges() - { - if (Definition.Principal.RequiresPassword()) - throw new SecurityException("Tasks which have been registered previously with stored passwords must use the TaskFolder.RegisterTaskDefinition method for updates."); - if (v2Task != null) - TaskService.GetFolder(System.IO.Path.GetDirectoryName(Path)).RegisterTaskDefinition(Name, Definition, TaskCreation.Update, null, null, Definition.Principal.LogonType); - else - TaskService.RootFolder.RegisterTaskDefinition(Name, Definition); - } - - /// Runs the registered task immediately. - /// - /// - /// The parameters used as values in the task actions. A maximum of 32 parameters can be supplied. To run a task with no parameters, - /// call this method without any values (e.g. - /// Run() - /// ). - /// - /// - /// The string values that you specify are paired with names and stored as name-value pairs. If you specify a single string value, - /// then Arg0 will be the name assigned to the value. The value can be used in the task action where the $(Arg0) variable is used in - /// the action properties. - /// - /// - /// If you pass in values such as "0", "100", and "250" as an array of string values, then "0" will replace the $(Arg0) variables, - /// "100" will replace the $(Arg1) variables, and "250" will replace the $(Arg2) variables used in the action properties. - /// - /// - /// For more information and a list of action properties that can use $(Arg0), $(Arg1), ..., $(Arg32) variables in their values, see - /// Task Actions. - /// - /// - /// A instance that defines the new instance of the task. - /// - /// - /// - /// - /// - public RunningTask Run(params string[] parameters) - { - if (v2Task != null) - { - if (parameters.Length > 32) - throw new ArgumentOutOfRangeException(nameof(parameters), "A maximum of 32 values is allowed."); - if (TaskService.HighestSupportedVersion < TaskServiceVersion.V1_5 && parameters.Any(p => (p?.Length ?? 0) >= 260)) - throw new ArgumentOutOfRangeException(nameof(parameters), "On systems prior to Windows 10, all individual parameters must be less than 260 characters."); - var irt = v2Task.Run(parameters.Length == 0 ? null : parameters); - return irt != null ? new RunningTask(TaskService, v2Task, irt) : null; - } - - v1Task.Run(); - return new RunningTask(TaskService, v1Task); - } - - /// Runs the registered task immediately using specified flags and a session identifier. - /// Defines how the task is run. - /// - /// The terminal server session in which you want to start the task. - /// - /// If the value is not passed into the parameter, then the value - /// specified in this parameter is ignored.If the value is passed into the flags parameter - /// and the sessionID value is less than or equal to 0, then an invalid argument error will be returned. - /// - /// - /// If the value is passed into the parameter and the sessionID - /// value is a valid session ID greater than 0 and if no value is specified for the user parameter, then the Task Scheduler service - /// will try to start the task interactively as the user who is logged on to the specified session. - /// - /// - /// If the value is passed into the parameter and the sessionID - /// value is a valid session ID greater than 0 and if a user is specified in the user parameter, then the Task Scheduler service - /// will try to start the task interactively as the user who is specified in the user parameter. - /// - /// - /// The user for which the task runs. - /// - /// - /// The parameters used as values in the task actions. A maximum of 32 parameters can be supplied. To run a task with no parameters, - /// call this method without any values (e.g. - /// RunEx(0, 0, "MyUserName") - /// ). - /// - /// - /// The string values that you specify are paired with names and stored as name-value pairs. If you specify a single string value, - /// then Arg0 will be the name assigned to the value. The value can be used in the task action where the $(Arg0) variable is used in - /// the action properties. - /// - /// - /// If you pass in values such as "0", "100", and "250" as an array of string values, then "0" will replace the $(Arg0) variables, - /// "100" will replace the $(Arg1) variables, and "250" will replace the $(Arg2) variables used in the action properties. - /// - /// - /// For more information and a list of action properties that can use $(Arg0), $(Arg1), ..., $(Arg32) variables in their values, see - /// Task Actions. - /// - /// - /// A instance that defines the new instance of the task. - /// - /// - /// This method will return without error, but the task will not run if the AllowDemandStart property of ITaskSettings is set to - /// false for the task. - /// - /// If RunEx is invoked from a disabled task, it will return null and the task will not be run. - /// - /// Not supported under Task Scheduler 1.0. - /// - /// - /// - /// - /// - public RunningTask RunEx(TaskRunFlags flags, int sessionID, string user, params string[] parameters) - { - if (v2Task == null) throw new NotV1SupportedException(); - if (parameters == null || parameters.Any(s => s == null)) - throw new ArgumentNullException(nameof(parameters), "The array and none of the values passed as parameters may be `null`."); - if (parameters.Length > 32) - throw new ArgumentOutOfRangeException(nameof(parameters), "A maximum of 32 parameters can be supplied to RunEx."); - if (TaskService.HighestSupportedVersion < TaskServiceVersion.V1_5 && parameters.Any(p => (p?.Length ?? 0) >= 260)) - throw new ArgumentOutOfRangeException(nameof(parameters), "On systems prior to Windows 10, no individual parameter may be more than 260 characters."); - var irt = v2Task.RunEx(parameters.Length == 0 ? null : parameters, (int)flags, sessionID, user); - return irt != null ? new RunningTask(TaskService, v2Task, irt) : null; - } - - /// - /// Applies access control list (ACL) entries described by a object to the file described by the current - /// object. - /// - /// - /// A object that describes an access control list (ACL) entry to apply to the current task. - /// - /// - /// Give read access to all authenticated users for a task. - /// - /// - /// - /// - public void SetAccessControl([NotNull] TaskSecurity taskSecurity) => taskSecurity.Persist(this); - - /// Sets the security descriptor for the task. Not available to Task Scheduler 1.0. - /// The security descriptor for the task. - /// Flags that specify how to set the security descriptor. - /// Not supported under Task Scheduler 1.0. - public void SetSecurityDescriptorSddlForm([NotNull] string sddlForm, TaskSetSecurityOptions options = TaskSetSecurityOptions.None) - { - if (v2Task != null) - v2Task.SetSecurityDescriptor(sddlForm, (int)options); - else - throw new NotV1SupportedException(); - } - - /// Dynamically tries to load the assembly for the editor and displays it as editable for this task. - /// true if editor returns with OK response; false otherwise. - /// - /// The Microsoft.Win32.TaskSchedulerEditor.dll assembly must reside in the same directory as the Microsoft.Win32.TaskScheduler.dll - /// or in the GAC. - /// - public bool ShowEditor() - { - try - { - var t = ReflectionHelper.LoadType("Microsoft.Win32.TaskScheduler.TaskEditDialog", "Microsoft.Win32.TaskSchedulerEditor.dll"); - if (t != null) - return ReflectionHelper.InvokeMethod(t, new object[] { this, true, true }, "ShowDialog") == 1; - } - catch { } - return false; - } - - /// Shows the property page for the task (v1.0 only). - public void ShowPropertyPage() - { - if (v1Task != null) - v1Task.EditWorkItem(IntPtr.Zero, 0); - else - throw new NotV2SupportedException(); - } - - /// Stops the registered task immediately. - /// - /// The Stop method stops all instances of the task. - /// - /// System account users can stop a task, users with Administrator group privileges can stop a task, and if a user has rights to - /// execute and read a task, then the user can stop the task. A user can stop the task instances that are running under the same - /// credentials as the user account. In all other cases, the user is denied access to stop the task. - /// - /// - public void Stop() - { - if (v2Task != null) - v2Task.Stop(0); - else - v1Task.Terminate(); - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() => Name; - - int IComparable.CompareTo(object other) => CompareTo(other as Task); - - internal static Task CreateTask(TaskService svc, IRegisteredTask iTask, bool throwError = false) - { - var iDef = GetV2Definition(svc, iTask, throwError); - if (iDef != null || !svc.AllowReadOnlyTasks) return new Task(svc, iTask, iDef); - iDef = GetV2StrippedDefinition(svc, iTask); - return new Task(svc, iTask, iDef) { ReadOnly = true }; - } - - internal static int GetOSLibraryMinorVersion() => TaskService.LibraryVersion.Minor; - - [NotNull] - internal static string GetV1Path(ITask v1Task) - { - var iFile = (IPersistFile)v1Task; - iFile.GetCurFile(out var fileName); - return fileName ?? string.Empty; - } - - /// - /// Gets the ITaskDefinition for a V2 task and prevents the errors that come when connecting remotely to a higher version of the - /// Task Scheduler. - /// - /// The local task service. - /// The task instance. - /// if set to true this method will throw an exception if unable to get the task definition. - /// A valid ITaskDefinition that should not throw errors on the local instance. - /// Unable to get a compatible task definition for this version of the library. - internal static ITaskDefinition GetV2Definition(TaskService svc, IRegisteredTask iTask, bool throwError = false) - { - var xmlVer = new Version(); - try - { - var dd = new DefDoc(iTask.Xml); - xmlVer = dd.Version; - if (xmlVer.Minor > osLibMinorVer) - { - var newMinor = xmlVer.Minor; - if (!dd.Contains("Volatile", "false", true) && - !dd.Contains("MaintenanceSettings")) - newMinor = 3; - if (!dd.Contains("UseUnifiedSchedulingEngine", "false", true) && - !dd.Contains("DisallowStartOnRemoteAppSession", "false", true) && - !dd.Contains("RequiredPrivileges") && - !dd.Contains("ProcessTokenSidType", "Default", true)) - newMinor = 2; - if (!dd.Contains("DisplayName") && - !dd.Contains("GroupId") && - !dd.Contains("RunLevel", "LeastPrivilege", true) && - !dd.Contains("SecurityDescriptor") && - !dd.Contains("Source") && - !dd.Contains("URI") && - !dd.Contains("AllowStartOnDemand", "true", true) && - !dd.Contains("AllowHardTerminate", "true", true) && - !dd.Contains("MultipleInstancesPolicy", "IgnoreNew", true) && - !dd.Contains("NetworkSettings") && - !dd.Contains("StartWhenAvailable", "false", true) && - !dd.Contains("SendEmail") && - !dd.Contains("ShowMessage") && - !dd.Contains("ComHandler") && - !dd.Contains("EventTrigger") && - !dd.Contains("SessionStateChangeTrigger") && - !dd.Contains("RegistrationTrigger") && - !dd.Contains("RestartOnFailure") && - !dd.Contains("LogonType", "None", true)) - newMinor = 1; - - if (newMinor > osLibMinorVer && throwError) - throw new InvalidOperationException($"The current version of the native library (1.{osLibMinorVer}) does not support the original or minimum version of the \"{iTask.Name}\" task ({xmlVer}/1.{newMinor}). This is likely due to attempting to read the remote tasks of a newer version of Windows from a down-level client."); - - if (newMinor != xmlVer.Minor) - { - dd.Version = new Version(1, newMinor); - var def = svc.v2TaskService.NewTask(0); - def.XmlText = dd.Xml; - return def; - } - } - return iTask.Definition; - } - catch (COMException comEx) - { - if (throwError) - { - if ((uint)comEx.ErrorCode == 0x80041318 && xmlVer.Minor != osLibMinorVer) // Incorrect XML value - throw new InvalidOperationException($"The current version of the native library (1.{osLibMinorVer}) does not support the version of the \"{iTask.Name}\" task ({xmlVer})"); - throw; - } - } - catch - { - if (throwError) - throw; - } - return null; - } - - internal static ITaskDefinition GetV2StrippedDefinition(TaskService svc, IRegisteredTask iTask) - { - try - { - var dd = new DefDoc(iTask.Xml); - var xmlVer = dd.Version; - if (xmlVer.Minor > osLibMinorVer) - { - if (osLibMinorVer < 4) - { - dd.RemoveTag("Volatile"); - dd.RemoveTag("MaintenanceSettings"); - } - if (osLibMinorVer < 3) - { - dd.RemoveTag("UseUnifiedSchedulingEngine"); - dd.RemoveTag("DisallowStartOnRemoteAppSession"); - dd.RemoveTag("RequiredPrivileges"); - dd.RemoveTag("ProcessTokenSidType"); - } - if (osLibMinorVer < 2) - { - dd.RemoveTag("DisplayName"); - dd.RemoveTag("GroupId"); - dd.RemoveTag("RunLevel"); - dd.RemoveTag("SecurityDescriptor"); - dd.RemoveTag("Source"); - dd.RemoveTag("URI"); - dd.RemoveTag("AllowStartOnDemand"); - dd.RemoveTag("AllowHardTerminate"); - dd.RemoveTag("MultipleInstancesPolicy"); - dd.RemoveTag("NetworkSettings"); - dd.RemoveTag("StartWhenAvailable"); - dd.RemoveTag("SendEmail"); - dd.RemoveTag("ShowMessage"); - dd.RemoveTag("ComHandler"); - dd.RemoveTag("EventTrigger"); - dd.RemoveTag("SessionStateChangeTrigger"); - dd.RemoveTag("RegistrationTrigger"); - dd.RemoveTag("RestartOnFailure"); - dd.RemoveTag("LogonType"); - } - dd.RemoveTag("WnfStateChangeTrigger"); // Remove custom triggers that can't be sent to Xml - dd.Version = new Version(1, osLibMinorVer); - var def = svc.v2TaskService.NewTask(0); -#if DEBUG - var path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), - $"TS_Stripped_Def_{xmlVer.Minor}-{osLibMinorVer}_{iTask.Name}.xml"); - File.WriteAllText(path, dd.Xml, Encoding.Unicode); -#endif - def.XmlText = dd.Xml; - return def; - } - } - catch (Exception ex) - { - Debug.WriteLine($"Error in GetV2StrippedDefinition: {ex}"); -#if DEBUG - throw; -#endif - } - return iTask.Definition; - } - - internal static TimeSpan StringToTimeSpan(string input) - { - if (!string.IsNullOrEmpty(input)) - try { return XmlConvert.ToTimeSpan(input); } catch { } - return TimeSpan.Zero; - } - - internal static string TimeSpanToString(TimeSpan span) - { - if (span != TimeSpan.Zero) - try { return XmlConvert.ToString(span); } catch { } - return null; - } - - internal void V1Reactivate() - { - var iTask = TaskService.GetTask(TaskService.v1TaskScheduler, Name); - if (iTask != null) - v1Task = iTask; - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - protected void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - private class DefDoc - { - private readonly XmlDocument doc; - - public DefDoc(string xml) - { - doc = new XmlDocument(); - doc.LoadXml(xml); - } - - public Version Version - { - get - { - try - { - return new Version(doc["Task"].Attributes["version"].Value); - } - catch - { - throw new InvalidOperationException("Task definition does not contain a version."); - } - } - set - { - var task = doc["Task"]; - if (task != null) task.Attributes["version"].Value = value.ToString(2); - } - } - - public string Xml => doc.OuterXml; - - public bool Contains(string tag, string defaultVal = null, bool removeIfFound = false) - { - var nl = doc.GetElementsByTagName(tag); - while (nl.Count > 0) - { - var e = nl[0]; - if (e.InnerText != defaultVal || !removeIfFound || e.ParentNode == null) - return true; - e.ParentNode?.RemoveChild(e); - nl = doc.GetElementsByTagName(tag); - } - return false; - } - - public void RemoveTag(string tag) - { - var nl = doc.GetElementsByTagName(tag); - while (nl.Count > 0) - { - var e = nl[0]; - e.ParentNode?.RemoveChild(e); - nl = doc.GetElementsByTagName(tag); - } - } - } - } - - /// Contains information about the compatibility of the current configuration with a specified version. - [PublicAPI] - public class TaskCompatibilityEntry - { - internal TaskCompatibilityEntry(TaskCompatibility comp, string prop, string reason) - { - CompatibilityLevel = comp; - Property = prop; - Reason = reason; - } - - /// Gets the compatibility level. - /// The compatibility level. - public TaskCompatibility CompatibilityLevel { get; } - - /// Gets the property name with the incompatibility. - /// The property name. - public string Property { get; } - - /// Gets the reason for the incompatibility. - /// The reason. - public string Reason { get; } - } - - /// Defines all the components of a task, such as the task settings, triggers, actions, and registration information. - [XmlRoot("Task", Namespace = tns, IsNullable = false)] - [XmlSchemaProvider("GetV1SchemaFile")] - [PublicAPI, Serializable] - public sealed class TaskDefinition : IDisposable, IXmlSerializable, INotifyPropertyChanged - { - internal const string tns = "http://schemas.microsoft.com/windows/2004/02/mit/task"; - - internal string v1Name = string.Empty; - internal ITask v1Task; - internal ITaskDefinition v2Def; - - private ActionCollection actions; - private TaskPrincipal principal; - private TaskRegistrationInfo regInfo; - private TaskSettings settings; - private TriggerCollection triggers; - - internal TaskDefinition([NotNull] ITask iTask, string name) - { - v1Task = iTask; - v1Name = name; - } - - internal TaskDefinition([NotNull] ITaskDefinition iDef) => v2Def = iDef; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets a collection of actions that are performed by the task. - [XmlArrayItem(ElementName = "Exec", IsNullable = true, Type = typeof(Action.ExecAction))] - [XmlArrayItem(ElementName = "ShowMessage", IsNullable = true, Type = typeof(Action.ShowMessageAction))] - [XmlArrayItem(ElementName = "ComHandler", IsNullable = true, Type = typeof(Action.ComHandlerAction))] - [XmlArrayItem(ElementName = "SendEmail", IsNullable = true, Type = typeof(Action.EmailAction))] - [XmlArray] - [NotNull, ItemNotNull] - public ActionCollection Actions => actions ??= v2Def != null ? new ActionCollection(v2Def) : new ActionCollection(v1Task); - - /// - /// Gets or sets the data that is associated with the task. This data is ignored by the Task Scheduler service, but is used by - /// third-parties who wish to extend the task format. - /// - /// - /// For V1 tasks, this library makes special use of the SetWorkItemData and GetWorkItemData methods and does not expose that data - /// stream directly. Instead, it uses that data stream to hold a dictionary of properties that are not supported under V1, but can - /// have values under V2. An example of this is the value which is stored in the data stream. - /// - /// The library does not provide direct access to the V1 work item data. If using V2 properties with a V1 task, programmatic access - /// to the task using the native API will retrieve unreadable results from GetWorkItemData and will eliminate those property values - /// if SetWorkItemData is used. - /// - /// - [CanBeNull] - public string Data - { - get => v2Def != null ? v2Def.Data : v1Task.GetDataItem(nameof(Data)); - set - { - if (v2Def != null) - v2Def.Data = value; - else - v1Task.SetDataItem(nameof(Data), value); - OnNotifyPropertyChanged(); - } - } - - /// Gets the lowest supported version that supports the settings for this . - [XmlIgnore] - public TaskCompatibility LowestSupportedVersion => GetLowestSupportedVersion(); - - /// Gets a collection of triggers that are used to start a task. - [XmlArrayItem(ElementName = "BootTrigger", IsNullable = true, Type = typeof(BootTrigger))] - [XmlArrayItem(ElementName = "CalendarTrigger", IsNullable = true, Type = typeof(CalendarTrigger))] - [XmlArrayItem(ElementName = "IdleTrigger", IsNullable = true, Type = typeof(IdleTrigger))] - [XmlArrayItem(ElementName = "LogonTrigger", IsNullable = true, Type = typeof(LogonTrigger))] - [XmlArrayItem(ElementName = "TimeTrigger", IsNullable = true, Type = typeof(TimeTrigger))] - [XmlArray] - [NotNull, ItemNotNull] - public TriggerCollection Triggers => triggers ??= v2Def != null ? new TriggerCollection(v2Def) : new TriggerCollection(v1Task); - - /// Gets or sets the XML-formatted definition of the task. - [XmlIgnore] - public string XmlText - { - get => v2Def != null ? v2Def.XmlText : XmlSerializationHelper.WriteObjectToXmlText(this); - set - { - if (v2Def != null) - v2Def.XmlText = value; - else - XmlSerializationHelper.ReadObjectFromXmlText(value, this); - OnNotifyPropertyChanged(); - } - } - - /// Gets the principal for the task that provides the security credentials for the task. - [NotNull] - public TaskPrincipal Principal => principal ??= v2Def != null ? new TaskPrincipal(v2Def.Principal, () => XmlText) : new TaskPrincipal(v1Task); - - /// - /// Gets a class instance of registration information that is used to describe a task, such as the description of the task, the - /// author of the task, and the date the task is registered. - /// - public TaskRegistrationInfo RegistrationInfo => regInfo ??= v2Def != null ? new TaskRegistrationInfo(v2Def.RegistrationInfo) : new TaskRegistrationInfo(v1Task); - - /// Gets the settings that define how the Task Scheduler service performs the task. - [NotNull] - public TaskSettings Settings => settings ??= v2Def != null ? new TaskSettings(v2Def.Settings) : new TaskSettings(v1Task); - - /// Gets the XML Schema file for V1 tasks. - /// The for V1 tasks. - /// An object containing the XML Schema for V1 tasks. - public static XmlSchemaComplexType GetV1SchemaFile([NotNull] XmlSchemaSet xs) - { - XmlSchema schema; - using (var xsdFile = Assembly.GetAssembly(typeof(TaskDefinition)).GetManifestResourceStream("Microsoft.Win32.TaskScheduler.V1.TaskSchedulerV1Schema.xsd")) - { - var schemaSerializer = new XmlSerializer(typeof(XmlSchema)); - schema = (XmlSchema)schemaSerializer.Deserialize(XmlReader.Create(xsdFile)); - xs.Add(schema); - } - - // target namespace - var name = new XmlQualifiedName("taskType", tns); - var productType = (XmlSchemaComplexType)schema.SchemaTypes[name]; - - return productType; - } - - /// - /// Determines whether this can use the Unified Scheduling Engine or if it contains unsupported properties. - /// - /// - /// if set to true throws an with details about unsupported properties in the Data - /// property of the exception. - /// - /// - /// true if this can use the Unified Scheduling Engine; otherwise, false. - public bool CanUseUnifiedSchedulingEngine(bool throwExceptionWithDetails = false, Version taskSchedulerVersion = null) - { - var tsVer = taskSchedulerVersion ?? TaskService.LibraryVersion; - if (tsVer < TaskServiceVersion.V1_3) return false; - var ex = new InvalidOperationException { HelpLink = "http://msdn.microsoft.com/en-us/library/windows/desktop/aa384138(v=vs.85).aspx" }; - var bad = false; - /*if (Principal.LogonType == TaskLogonType.InteractiveTokenOrPassword) - { - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, "Principal.LogonType", "== TaskLogonType.InteractiveTokenOrPassword"); - } - if (Settings.MultipleInstances == TaskInstancesPolicy.StopExisting) - { - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, "Settings.MultipleInstances", "== TaskInstancesPolicy.StopExisting"); - }*/ - if (Settings.NetworkSettings.Id != Guid.Empty && tsVer >= TaskServiceVersion.V1_5) - { - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, "Settings.NetworkSettings.Id", "!= Guid.Empty"); - } - /*if (!Settings.AllowHardTerminate) - { - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, "Settings.AllowHardTerminate", "== false"); - }*/ - if (!Actions.PowerShellConversion.IsFlagSet(PowerShellActionPlatformOption.Version2)) - for (var i = 0; i < Actions.Count; i++) - { - var a = Actions[i]; - switch (a) - { - case Action.EmailAction _: - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Actions[{i}]", "== typeof(EmailAction)"); - break; - - case Action.ShowMessageAction _: - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Actions[{i}]", "== typeof(ShowMessageAction)"); - break; - } - } - if (tsVer == TaskServiceVersion.V1_3) - for (var i = 0; i < Triggers.Count; i++) - { - Trigger t; - try { t = Triggers[i]; } - catch - { - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Triggers[{i}]", "is irretrievable."); - continue; - } - switch (t) - { - case MonthlyTrigger _: - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Triggers[{i}]", "== typeof(MonthlyTrigger)"); - break; - - case MonthlyDOWTrigger _: - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Triggers[{i}]", "== typeof(MonthlyDOWTrigger)"); - break; - /*case ICalendarTrigger _ when t.Repetition.IsSet(): - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Triggers[{i}].Repetition", ""); - break; - - case EventTrigger _ when ((EventTrigger)t).ValueQueries.Count > 0: - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Triggers[{i}].ValueQueries.Count", "!= 0"); - break;*/ - } - if (t.ExecutionTimeLimit != TimeSpan.Zero) - { - bad = true; - if (!throwExceptionWithDetails) return false; - TryAdd(ex.Data, $"Triggers[{i}].ExecutionTimeLimit", "!= TimeSpan.Zero"); - } - } - if (bad && throwExceptionWithDetails) - throw ex; - return !bad; - } - - /// Releases all resources used by this class. - public void Dispose() - { - regInfo = null; - triggers = null; - settings = null; - principal = null; - actions = null; - if (v2Def != null) Marshal.ReleaseComObject(v2Def); - v1Task = null; - } - - /// Validates the current . - /// - /// if set to true throw a with details about invalid properties. - /// - /// true if current is valid; false if not. - public bool Validate(bool throwException = false) - { - var ex = new InvalidOperationException(); - if (Settings.UseUnifiedSchedulingEngine) - { - try { CanUseUnifiedSchedulingEngine(throwException); } - catch (InvalidOperationException iox) - { - foreach (DictionaryEntry kvp in iox.Data) - TryAdd(ex.Data, (kvp.Key as ICloneable)?.Clone() ?? kvp.Key, (kvp.Value as ICloneable)?.Clone() ?? kvp.Value); - } - } - - if (Settings.Compatibility >= TaskCompatibility.V2_2) - { - var PT1D = TimeSpan.FromDays(1); - if (Settings.MaintenanceSettings.IsSet() && (Settings.MaintenanceSettings.Period < PT1D || Settings.MaintenanceSettings.Deadline < PT1D || Settings.MaintenanceSettings.Deadline <= Settings.MaintenanceSettings.Period)) - TryAdd(ex.Data, "Settings.MaintenanceSettings", "Period or Deadline must be at least 1 day and Deadline must be greater than Period."); - } - - var list = new List(); - if (GetLowestSupportedVersion(list) > Settings.Compatibility) - foreach (var item in list) - TryAdd(ex.Data, item.Property, item.Reason); - - var startWhenAvailable = Settings.StartWhenAvailable; - var delOldTask = Settings.DeleteExpiredTaskAfter != TimeSpan.Zero; - var v1 = Settings.Compatibility < TaskCompatibility.V2; - var hasEndBound = false; - for (var i = 0; i < Triggers.Count; i++) - { - Trigger trigger; - try { trigger = Triggers[i]; } - catch - { - TryAdd(ex.Data, $"Triggers[{i}]", "is irretrievable."); - continue; - } - if (startWhenAvailable && trigger.Repetition.Duration != TimeSpan.Zero && trigger.EndBoundary == DateTime.MaxValue) - TryAdd(ex.Data, "Settings.StartWhenAvailable", "== true requires time-based tasks with an end boundary or time-based tasks that are set to repeat infinitely."); - if (v1 && trigger.Repetition.Interval != TimeSpan.Zero && trigger.Repetition.Interval >= trigger.Repetition.Duration) - TryAdd(ex.Data, "Trigger.Repetition.Interval", ">= Trigger.Repetition.Duration under Task Scheduler 1.0."); - if (trigger.EndBoundary <= trigger.StartBoundary) - TryAdd(ex.Data, "Trigger.StartBoundary", ">= Trigger.EndBoundary is not allowed."); - if (delOldTask && trigger.EndBoundary != DateTime.MaxValue) - hasEndBound = true; - } - if (delOldTask && !hasEndBound) - TryAdd(ex.Data, "Settings.DeleteExpiredTaskAfter", "!= TimeSpan.Zero requires at least one trigger with an end boundary."); - - if (throwException && ex.Data.Count > 0) - throw ex; - return ex.Data.Count == 0; - } - - XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(XmlReader reader) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), tns); - XmlSerializationHelper.ReadObjectProperties(reader, this); - reader.ReadEndElement(); - } - - void IXmlSerializable.WriteXml(XmlWriter writer) => - // TODO:FIX writer.WriteAttributeString("version", "1.1"); - XmlSerializationHelper.WriteObjectProperties(writer, this); - - internal static Dictionary GetV1TaskDataDictionary(ITask v1Task) - { - Dictionary dict; - var o = GetV1TaskData(v1Task); - if (o is string) - dict = new Dictionary(2) { { "Data", o.ToString() }, { "Documentation", o.ToString() } }; - else - dict = o as Dictionary; - return dict ?? new Dictionary(); - } - - internal static void SetV1TaskData(ITask v1Task, object value) - { - if (value == null) - v1Task.SetWorkItemData(0, null); - else - { - var b = new BinaryFormatter(); - var stream = new MemoryStream(); - b.Serialize(stream, value); - v1Task.SetWorkItemData((ushort)stream.Length, stream.ToArray()); - } - } - - internal void V1Save(string newName) - { - if (v1Task != null) - { - Triggers.Bind(); - - var iFile = (IPersistFile)v1Task; - if (string.IsNullOrEmpty(newName) || newName == v1Name) - { - try - { - iFile.Save(null, false); - iFile = null; - return; - } - catch { } - } - - iFile.GetCurFile(out var path); - File.Delete(path); - path = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + newName + Path.GetExtension(path); - File.Delete(path); - iFile.Save(path, true); - } - } - - private static object GetV1TaskData(ITask v1Task) - { - var Data = IntPtr.Zero; - try - { - v1Task.GetWorkItemData(out var DataLen, out Data); - if (DataLen == 0) - return null; - var bytes = new byte[DataLen]; - Marshal.Copy(Data, bytes, 0, DataLen); - var stream = new MemoryStream(bytes, false); - var b = new BinaryFormatter(); - return b.Deserialize(stream); - } - catch { } - finally - { - if (Data != IntPtr.Zero) - Marshal.FreeCoTaskMem(Data); - } - return null; - } - - private static void TryAdd(IDictionary d, object k, object v) - { - if (!d.Contains(k)) - d.Add(k, v); - } - - /// Gets the lowest supported version. - /// The output list. - /// - private TaskCompatibility GetLowestSupportedVersion(IList outputList = null) - { - var res = TaskCompatibility.V1; - var list = new List(); - - //if (Principal.DisplayName != null) - // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.DisplayName", "cannot have a value.")); } - if (Principal.GroupId != null) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.GroupId", "cannot have a value.")); } - //this.Principal.Id != null || - if (Principal.LogonType == TaskLogonType.Group || Principal.LogonType == TaskLogonType.None || Principal.LogonType == TaskLogonType.S4U) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.LogonType", "cannot be Group, None or S4U.")); } - if (Principal.RunLevel == TaskRunLevel.Highest) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Principal.RunLevel", "cannot be set to Highest.")); } - if (RegistrationInfo.SecurityDescriptorSddlForm != null) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.SecurityDescriptorSddlForm", "cannot have a value.")); } - //if (RegistrationInfo.Source != null) - // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.Source", "cannot have a value.")); } - //if (RegistrationInfo.URI != null) - // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.URI", "cannot have a value.")); } - //if (RegistrationInfo.Version != new Version(1, 0)) - // { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "RegistrationInfo.Version", "cannot be set or equal 1.0.")); } - if (Settings.AllowDemandStart == false) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.AllowDemandStart", "must be true.")); } - if (Settings.AllowHardTerminate == false) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.AllowHardTerminate", "must be true.")); } - if (Settings.MultipleInstances != TaskInstancesPolicy.IgnoreNew) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.MultipleInstances", "must be set to IgnoreNew.")); } - if (Settings.NetworkSettings.IsSet()) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.NetworkSetting", "cannot have a value.")); } - if (Settings.RestartCount != 0) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.RestartCount", "must be 0.")); } - if (Settings.RestartInterval != TimeSpan.Zero) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.RestartInterval", "must be 0 (TimeSpan.Zero).")); } - if (Settings.StartWhenAvailable) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Settings.StartWhenAvailable", "must be false.")); } - - if ((Actions.PowerShellConversion & PowerShellActionPlatformOption.Version1) != PowerShellActionPlatformOption.Version1 && (Actions.ContainsType(typeof(Action.EmailAction)) || Actions.ContainsType(typeof(Action.ShowMessageAction)) || Actions.ContainsType(typeof(Action.ComHandlerAction)))) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Actions", "may only contain ExecAction types unless Actions.PowerShellConversion includes Version1.")); } - if ((Actions.PowerShellConversion & PowerShellActionPlatformOption.Version2) != PowerShellActionPlatformOption.Version2 && (Actions.ContainsType(typeof(Action.EmailAction)) || Actions.ContainsType(typeof(Action.ShowMessageAction)))) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Actions", "may only contain ExecAction and ComHanlderAction types unless Actions.PowerShellConversion includes Version2.")); } - - try - { - if (null != Triggers.Find(t => t is ITriggerDelay && ((ITriggerDelay)t).Delay != TimeSpan.Zero)) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain delays.")); } - if (null != Triggers.Find(t => t.ExecutionTimeLimit != TimeSpan.Zero || t.Id != null)) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain an ExecutionTimeLimit or Id.")); } - if (null != Triggers.Find(t => (t as LogonTrigger)?.UserId != null)) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain a LogonTrigger with a UserId.")); } - if (null != Triggers.Find(t => t is MonthlyDOWTrigger && ((MonthlyDOWTrigger)t).RunOnLastWeekOfMonth || t is MonthlyDOWTrigger && (((MonthlyDOWTrigger)t).WeeksOfMonth & (((MonthlyDOWTrigger)t).WeeksOfMonth - 1)) != 0)) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain a MonthlyDOWTrigger with RunOnLastWeekOfMonth set or multiple WeeksOfMonth.")); } - if (null != Triggers.Find(t => t is MonthlyTrigger && ((MonthlyTrigger)t).RunOnLastDayOfMonth)) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain a MonthlyTrigger with RunOnLastDayOfMonth set.")); } - if (Triggers.ContainsType(typeof(EventTrigger)) || Triggers.ContainsType(typeof(SessionStateChangeTrigger)) || Triggers.ContainsType(typeof(RegistrationTrigger))) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain EventTrigger, SessionStateChangeTrigger, or RegistrationTrigger types.")); } - } - catch - { - list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2, "Triggers", "cannot contain Custom triggers.")); - } - - if (Principal.ProcessTokenSidType != TaskProcessTokenSidType.Default) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Principal.ProcessTokenSidType", "must be Default.")); } - if (Principal.RequiredPrivileges.Count > 0) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Principal.RequiredPrivileges", "must be empty.")); } - if (Settings.DisallowStartOnRemoteAppSession) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Settings.DisallowStartOnRemoteAppSession", "must be false.")); } - if (Settings.UseUnifiedSchedulingEngine) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_1, "Settings.UseUnifiedSchedulingEngine", "must be false.")); } - - if (Settings.MaintenanceSettings.IsSet()) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_2, "this.Settings.MaintenanceSettings", "must have no values set.")); } - if (Settings.Volatile) - { list.Add(new TaskCompatibilityEntry(TaskCompatibility.V2_2, " this.Settings.Volatile", "must be false.")); } - - foreach (var item in list) - { - if (res < item.CompatibilityLevel) res = item.CompatibilityLevel; - outputList?.Add(item); - } - return res; - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - /// - /// Provides the security credentials for a principal. These security credentials define the security context for the tasks that are - /// associated with the principal. - /// - [XmlRoot("Principals", Namespace = TaskDefinition.tns, IsNullable = true)] - [PublicAPI] - public sealed class TaskPrincipal : IDisposable, IXmlSerializable, INotifyPropertyChanged - { - private const string localSystemAcct = "SYSTEM"; - private readonly IPrincipal v2Principal; - private readonly IPrincipal2 v2Principal2; - private readonly Func xmlFunc; - private TaskPrincipalPrivileges reqPriv; - private string v1CachedAcctInfo; - private ITask v1Task; - - internal TaskPrincipal([NotNull] IPrincipal iPrincipal, Func defXml) - { - xmlFunc = defXml; - v2Principal = iPrincipal; - try { if (Environment.OSVersion.Version >= new Version(6, 1)) v2Principal2 = (IPrincipal2)v2Principal; } - catch { } - } - - internal TaskPrincipal([NotNull] ITask iTask) => v1Task = iTask; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Gets the account associated with this principal. This value is pulled from the TaskDefinition's XMLText property if set. - /// - /// The account. - [DefaultValue(null), Browsable(false)] - public string Account - { - get - { - try - { - var xml = xmlFunc?.Invoke(); - if (!string.IsNullOrEmpty(xml)) - { - var doc = new XmlDocument(); - doc.LoadXml(xml); - var pn = doc.DocumentElement?["Principals"]?["Principal"]; - if (pn != null) - { - var un = pn["UserId"] ?? pn["GroupId"]; - if (un != null) - try { return User.FromSidString(un.InnerText).Name; } - catch - { - try { return new User(un.InnerText).Name; } - catch { } - } - } - } - return new User(ToString()).Name; - } - catch - { - return null; - } - } - } - - /// Gets or sets the name of the principal that is displayed in the Task Scheduler UI. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - public string DisplayName - { - get => v2Principal != null ? v2Principal.DisplayName : v1Task.GetDataItem("PrincipalDisplayName"); - set - { - if (v2Principal != null) - v2Principal.DisplayName = value; - else - v1Task.SetDataItem("PrincipalDisplayName", value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the identifier of the user group that is required to run the tasks that are associated with the principal. Setting - /// this property to something other than a null or empty string, will set the property to NULL and will set - /// the property to TaskLogonType.Group; - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - [XmlIgnore] - public string GroupId - { - get => v2Principal?.GroupId; - set - { - if (v2Principal != null) - { - if (string.IsNullOrEmpty(value)) - value = null; - else - { - v2Principal.UserId = null; - v2Principal.LogonType = TaskLogonType.Group; - } - v2Principal.GroupId = value; - } - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the identifier of the principal. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - [XmlAttribute(AttributeName = "id", DataType = "ID")] - public string Id - { - get => v2Principal != null ? v2Principal.Id : v1Task.GetDataItem("PrincipalId"); - set - { - if (v2Principal != null) - v2Principal.Id = value; - else - v1Task.SetDataItem("PrincipalId", value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the security logon method that is required to run the tasks that are associated with the principal. - /// - /// TaskLogonType values of Group, None, or S4UNot are not supported under Task Scheduler 1.0. - /// - [DefaultValue(typeof(TaskLogonType), "None")] - public TaskLogonType LogonType - { - get - { - if (v2Principal != null) - return v2Principal.LogonType; - if (UserId == localSystemAcct) - return TaskLogonType.ServiceAccount; - if (v1Task.HasFlags(TaskFlags.RunOnlyIfLoggedOn)) - return TaskLogonType.InteractiveToken; - return TaskLogonType.InteractiveTokenOrPassword; - } - set - { - if (v2Principal != null) - v2Principal.LogonType = value; - else - { - if (value == TaskLogonType.Group || value == TaskLogonType.None || value == TaskLogonType.S4U) - throw new NotV1SupportedException(); - v1Task.SetFlags(TaskFlags.RunOnlyIfLoggedOn, value == TaskLogonType.InteractiveToken); - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the task process security identifier (SID) type. - /// One of the enumeration constants. - /// Setting this value appears to break the Task Scheduler MMC and does not output in XML. Removed to prevent problems. - /// Not supported under Task Scheduler versions prior to 2.1. - [XmlIgnore, DefaultValue(typeof(TaskProcessTokenSidType), "Default")] - public TaskProcessTokenSidType ProcessTokenSidType - { - get => v2Principal2?.ProcessTokenSidType ?? TaskProcessTokenSidType.Default; - set - { - if (v2Principal2 != null) - v2Principal2.ProcessTokenSidType = value; - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_1); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets the security credentials for a principal. These security credentials define the security context for the tasks that are - /// associated with the principal. - /// - /// Setting this value appears to break the Task Scheduler MMC and does not output in XML. Removed to prevent problems. - [XmlIgnore] - public TaskPrincipalPrivileges RequiredPrivileges => reqPriv ??= new TaskPrincipalPrivileges(v2Principal2); - - /// - /// Gets or sets the identifier that is used to specify the privilege level that is required to run the tasks that are associated - /// with the principal. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TaskRunLevel), "LUA")] - [XmlIgnore] - public TaskRunLevel RunLevel - { - get => v2Principal?.RunLevel ?? TaskRunLevel.LUA; - set - { - if (v2Principal != null) - v2Principal.RunLevel = value; - else if (value != TaskRunLevel.LUA) - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the user identifier that is required to run the tasks that are associated with the principal. Setting this property - /// to something other than a null or empty string, will set the property to NULL; - /// - [DefaultValue(null)] - public string UserId - { - get - { - if (v2Principal != null) - return v2Principal.UserId; - if (v1CachedAcctInfo == null) - { - try - { - string acct = v1Task.GetAccountInformation(); - v1CachedAcctInfo = string.IsNullOrEmpty(acct) ? localSystemAcct : acct; - } - catch { v1CachedAcctInfo = string.Empty; } - } - return v1CachedAcctInfo == string.Empty ? null : v1CachedAcctInfo; - } - set - { - if (v2Principal != null) - { - if (string.IsNullOrEmpty(value)) - value = null; - else - { - v2Principal.GroupId = null; - //if (value.Contains(@"\") && !value.Contains(@"\\")) - // value = value.Replace(@"\", @"\\"); - } - v2Principal.UserId = value; - } - else - { - if (value.Equals(localSystemAcct, StringComparison.CurrentCultureIgnoreCase)) - value = ""; - v1Task.SetAccountInformation(value, IntPtr.Zero); - v1CachedAcctInfo = null; - } - OnNotifyPropertyChanged(); - } - } - - /// Validates the supplied account against the supplied . - /// The user or group account name. - /// The SID type for the process. - /// true if supplied account can be used for the supplied SID type. - public static bool ValidateAccountForSidType(string acct, TaskProcessTokenSidType sidType) - { - string[] validUserIds = { "NETWORK SERVICE", "LOCAL SERVICE", "S-1-5-19", "S-1-5-20" }; - return sidType == TaskProcessTokenSidType.Default || Array.Find(validUserIds, id => id.Equals(acct, StringComparison.InvariantCultureIgnoreCase)) != null; - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Principal != null) - Marshal.ReleaseComObject(v2Principal); - v1Task = null; - } - - /// Gets a value indicating whether current Principal settings require a password to be provided. - /// true if settings requires a password to be provided; otherwise, false. - public bool RequiresPassword() => LogonType == TaskLogonType.InteractiveTokenOrPassword || - LogonType == TaskLogonType.Password || LogonType == TaskLogonType.S4U && UserId != null && string.Compare(UserId, WindowsIdentity.GetCurrent().Name, StringComparison.OrdinalIgnoreCase) != 0; - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() => LogonType == TaskLogonType.Group ? GroupId : UserId; - - XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(XmlReader reader) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); - if (reader.HasAttributes) - Id = reader.GetAttribute("id"); - reader.Read(); - while (reader.MoveToContent() == XmlNodeType.Element) - { - switch (reader.LocalName) - { - case "Principal": - reader.Read(); - XmlSerializationHelper.ReadObjectProperties(reader, this); - reader.ReadEndElement(); - break; - - default: - reader.Skip(); - break; - } - } - reader.ReadEndElement(); - } - - void IXmlSerializable.WriteXml(XmlWriter writer) - { - if (string.IsNullOrEmpty(ToString()) && LogonType == TaskLogonType.None) return; - writer.WriteStartElement("Principal"); - if (!string.IsNullOrEmpty(Id)) - writer.WriteAttributeString("id", Id); - XmlSerializationHelper.WriteObjectProperties(writer, this); - writer.WriteEndElement(); - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - /// - /// List of security credentials for a principal under version 1.3 of the Task Scheduler. These security credentials define the security - /// context for the tasks that are associated with the principal. - /// - [PublicAPI] - public sealed class TaskPrincipalPrivileges : IList - { - private readonly IPrincipal2 v2Principal2; - - internal TaskPrincipalPrivileges(IPrincipal2 iPrincipal2 = null) => v2Principal2 = iPrincipal2; - - /// Gets the number of elements contained in the . - /// The number of elements contained in the . - public int Count => v2Principal2?.RequiredPrivilegeCount ?? 0; - - /// Gets a value indicating whether the is read-only. - /// true if the is read-only; otherwise, false. - public bool IsReadOnly => false; - - /// Gets or sets the element at the specified index. - /// The element at the specified index. - /// is not a valid index in the . - /// - /// The property is set and the is read-only. - /// - public TaskPrincipalPrivilege this[int index] - { - get - { - if (v2Principal2 != null) - return (TaskPrincipalPrivilege)Enum.Parse(typeof(TaskPrincipalPrivilege), v2Principal2[index + 1]); - throw new IndexOutOfRangeException(); - } - set => throw new NotImplementedException(); - } - - /// Adds an item to the . - /// The object to add to the . - /// The is read-only. - public void Add(TaskPrincipalPrivilege item) - { - if (v2Principal2 != null) - v2Principal2.AddRequiredPrivilege(item.ToString()); - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_1); - } - - /// Determines whether the contains a specific value. - /// The object to locate in the . - /// - /// true if is found in the ; otherwise, false. - /// - public bool Contains(TaskPrincipalPrivilege item) => IndexOf(item) != -1; - - /// Copies to. - /// The array. - /// Index of the array. - public void CopyTo(TaskPrincipalPrivilege[] array, int arrayIndex) - { - using var pEnum = GetEnumerator(); - for (var i = arrayIndex; i < array.Length; i++) - { - if (!pEnum.MoveNext()) - break; - array[i] = pEnum.Current; - } - } - - /// Returns an enumerator that iterates through the collection. - /// A that can be used to iterate through the collection. - public IEnumerator GetEnumerator() => new TaskPrincipalPrivilegesEnumerator(v2Principal2); - - /// Determines the index of a specific item in the . - /// The object to locate in the . - /// The index of if found in the list; otherwise, -1. - public int IndexOf(TaskPrincipalPrivilege item) - { - for (var i = 0; i < Count; i++) - { - if (item == this[i]) - return i; - } - return -1; - } - - /// Removes all items from the . - /// The is read-only. - void ICollection.Clear() => throw new NotImplementedException(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Inserts an item to the at the specified index. - /// The zero-based index at which should be inserted. - /// The object to insert into the . - /// is not a valid index in the . - /// The is read-only. - void IList.Insert(int index, TaskPrincipalPrivilege item) => throw new NotImplementedException(); - - /// Removes the first occurrence of a specific object from the . - /// The object to remove from the . - /// - /// true if was successfully removed from the ; - /// otherwise, false. This method also returns false if is not found in the original . - /// - /// The is read-only. - bool ICollection.Remove(TaskPrincipalPrivilege item) => throw new NotImplementedException(); - - /// Removes the item at the specified index. - /// The zero-based index of the item to remove. - /// is not a valid index in the . - /// The is read-only. - void IList.RemoveAt(int index) => throw new NotImplementedException(); - - /// Enumerates the privileges set for a principal under version 1.3 of the Task Scheduler. - public sealed class TaskPrincipalPrivilegesEnumerator : IEnumerator - { - private readonly IPrincipal2 v2Principal2; - private int cur; - - internal TaskPrincipalPrivilegesEnumerator(IPrincipal2 iPrincipal2 = null) - { - v2Principal2 = iPrincipal2; - Reset(); - } - - /// Gets the element in the collection at the current position of the enumerator. - /// The element in the collection at the current position of the enumerator. - public TaskPrincipalPrivilege Current { get; private set; } - - object IEnumerator.Current => Current; - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() { } - - /// Advances the enumerator to the next element of the collection. - /// - /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. - /// - /// The collection was modified after the enumerator was created. - public bool MoveNext() - { - if (v2Principal2 != null && cur < v2Principal2.RequiredPrivilegeCount) - { - cur++; - Current = (TaskPrincipalPrivilege)Enum.Parse(typeof(TaskPrincipalPrivilege), v2Principal2[cur]); - return true; - } - Current = 0; - return false; - } - - /// Sets the enumerator to its initial position, which is before the first element in the collection. - /// The collection was modified after the enumerator was created. - public void Reset() - { - cur = 0; - Current = 0; - } - } - } - - /// - /// Provides the administrative information that can be used to describe the task. This information includes details such as a - /// description of the task, the author of the task, the date the task is registered, and the security descriptor of the task. - /// - [XmlRoot("RegistrationInfo", Namespace = TaskDefinition.tns, IsNullable = true)] - [PublicAPI] - public sealed class TaskRegistrationInfo : IDisposable, IXmlSerializable, INotifyPropertyChanged - { - private readonly IRegistrationInfo v2RegInfo; - private ITask v1Task; - - internal TaskRegistrationInfo([NotNull] IRegistrationInfo iRegInfo) => v2RegInfo = iRegInfo; - - internal TaskRegistrationInfo([NotNull] ITask iTask) => v1Task = iTask; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets or sets the author of the task. - [DefaultValue(null)] - public string Author - { - get => v2RegInfo != null ? v2RegInfo.Author : v1Task.GetCreator(); - set - { - if (v2RegInfo != null) - v2RegInfo.Author = value; - else - v1Task.SetCreator(value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the date and time when the task is registered. - [DefaultValue(typeof(DateTime), "0001-01-01T00:00:00")] - public DateTime Date - { - get - { - if (v2RegInfo != null) - { - if (DateTime.TryParse(v2RegInfo.Date, Trigger.DefaultDateCulture, DateTimeStyles.AssumeLocal, out var ret)) - return ret; - } - else - { - var v1Path = Task.GetV1Path(v1Task); - if (!string.IsNullOrEmpty(v1Path) && File.Exists(v1Path)) - return File.GetLastWriteTime(v1Path); - } - return DateTime.MinValue; - } - set - { - if (v2RegInfo != null) - v2RegInfo.Date = value == DateTime.MinValue ? null : value.ToString(Trigger.V2BoundaryDateFormat, Trigger.DefaultDateCulture); - else - { - var v1Path = Task.GetV1Path(v1Task); - if (!string.IsNullOrEmpty(v1Path) && File.Exists(v1Path)) - File.SetLastWriteTime(v1Path, value); - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the description of the task. - [DefaultValue(null)] - public string Description - { - get => v2RegInfo != null ? FixCrLf(v2RegInfo.Description) : v1Task.GetComment(); - set - { - if (v2RegInfo != null) - v2RegInfo.Description = value; - else - v1Task.SetComment(value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets any additional documentation for the task. - [DefaultValue(null)] - public string Documentation - { - get => v2RegInfo != null ? FixCrLf(v2RegInfo.Documentation) : v1Task.GetDataItem(nameof(Documentation)); - set - { - if (v2RegInfo != null) - v2RegInfo.Documentation = value; - else - v1Task.SetDataItem(nameof(Documentation), value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the security descriptor of the task. - /// The security descriptor. - [XmlIgnore] - public GenericSecurityDescriptor SecurityDescriptor - { - get => new RawSecurityDescriptor(SecurityDescriptorSddlForm); - set => SecurityDescriptorSddlForm = value?.GetSddlForm(Task.defaultAccessControlSections); - } - - /// Gets or sets the security descriptor of the task. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - [XmlIgnore] - public string SecurityDescriptorSddlForm - { - get - { - object sddl = null; - if (v2RegInfo != null) - sddl = v2RegInfo.SecurityDescriptor; - return sddl?.ToString(); - } - set - { - if (v2RegInfo != null) - v2RegInfo.SecurityDescriptor = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets where the task originated from. For example, a task may originate from a component, service, application, or user. - /// - [DefaultValue(null)] - public string Source - { - get => v2RegInfo != null ? v2RegInfo.Source : v1Task.GetDataItem(nameof(Source)); - set - { - if (v2RegInfo != null) - v2RegInfo.Source = value; - else - v1Task.SetDataItem(nameof(Source), value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the URI of the task. - /// - /// Note: Breaking change in version 2.0. This property was previously of type . It was found that in - /// Windows 8, many of the native tasks use this property in a string format rather than in a URI format. - /// - [DefaultValue(null)] - public string URI - { - get - { - var uri = v2RegInfo != null ? v2RegInfo.URI : v1Task.GetDataItem(nameof(URI)); - return string.IsNullOrEmpty(uri) ? null : uri; - } - set - { - if (v2RegInfo != null) - v2RegInfo.URI = value; - else - v1Task.SetDataItem(nameof(URI), value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the version number of the task. - [DefaultValueEx(typeof(Version), "1.0")] - public Version Version - { - get - { - var sver = v2RegInfo != null ? v2RegInfo.Version : v1Task.GetDataItem(nameof(Version)); - if (sver != null) try { return new Version(sver); } catch { } - return new Version(1, 0); - } - set - { - if (v2RegInfo != null) - v2RegInfo.Version = value?.ToString(); - else - v1Task.SetDataItem(nameof(Version), value.ToString()); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets an XML-formatted version of the registration information for the task. - [XmlIgnore] - public string XmlText - { - get => v2RegInfo != null ? v2RegInfo.XmlText : XmlSerializationHelper.WriteObjectToXmlText(this); - set - { - if (v2RegInfo != null) - v2RegInfo.XmlText = value; - else - XmlSerializationHelper.ReadObjectFromXmlText(value, this); - OnNotifyPropertyChanged(); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - v1Task = null; - if (v2RegInfo != null) - Marshal.ReleaseComObject(v2RegInfo); - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() - { - if (v2RegInfo != null || v1Task != null) - return DebugHelper.GetDebugString(this); - return base.ToString(); - } - - XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(XmlReader reader) - { - if (!reader.IsEmptyElement) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); - XmlSerializationHelper.ReadObjectProperties(reader, this, ProcessVersionXml); - reader.ReadEndElement(); - } - else - reader.Skip(); - } - - void IXmlSerializable.WriteXml(XmlWriter writer) => XmlSerializationHelper.WriteObjectProperties(writer, this, ProcessVersionXml); - - internal static string FixCrLf(string text) => text == null ? null : Regex.Replace(text, "(?Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - private bool ProcessVersionXml(PropertyInfo pi, object obj, ref object value) - { - if (pi.Name != "Version" || value == null) return false; - if (value is Version) - value = value.ToString(); - else if (value is string) - value = new Version(value.ToString()); - return true; - } - } - - /// Provides the settings that the Task Scheduler service uses to perform the task. - [XmlRoot("Settings", Namespace = TaskDefinition.tns, IsNullable = true)] - [PublicAPI] - public sealed class TaskSettings : IDisposable, IXmlSerializable, INotifyPropertyChanged - { - private const uint InfiniteRunTimeV1 = 0xFFFFFFFF; - - private readonly ITaskSettings v2Settings; - private readonly ITaskSettings2 v2Settings2; - private readonly ITaskSettings3 v2Settings3; - private IdleSettings idleSettings; - private MaintenanceSettings maintenanceSettings; - private NetworkSettings networkSettings; - private ITask v1Task; - - internal TaskSettings([NotNull] ITaskSettings iSettings) - { - v2Settings = iSettings; - try { if (Environment.OSVersion.Version >= new Version(6, 1)) v2Settings2 = (ITaskSettings2)v2Settings; } - catch { } - try { if (Environment.OSVersion.Version >= new Version(6, 2)) v2Settings3 = (ITaskSettings3)v2Settings; } - catch { } - } - - internal TaskSettings([NotNull] ITask iTask) => v1Task = iTask; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Gets or sets a Boolean value that indicates that the task can be started by using either the Run command or the Context menu. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(true)] - [XmlElement("AllowStartOnDemand")] - [XmlIgnore] - public bool AllowDemandStart - { - get => v2Settings == null || v2Settings.AllowDemandStart; - set - { - if (v2Settings != null) - v2Settings.AllowDemandStart = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the task may be terminated by using TerminateProcess. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(true)] - [XmlIgnore] - public bool AllowHardTerminate - { - get => v2Settings == null || v2Settings.AllowHardTerminate; - set - { - if (v2Settings != null) - v2Settings.AllowHardTerminate = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets an integer value that indicates which version of Task Scheduler a task is compatible with. - /// Not supported under Task Scheduler 1.0. - [XmlIgnore] - public TaskCompatibility Compatibility - { - get => v2Settings?.Compatibility ?? TaskCompatibility.V1; - set - { - if (v2Settings != null) - v2Settings.Compatibility = value; - else - if (value != TaskCompatibility.V1) - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the amount of time that the Task Scheduler will wait before deleting the task after it expires. If no value is - /// specified for this property, then the Task Scheduler service will not delete the task. - /// - /// - /// Gets and sets the amount of time that the Task Scheduler will wait before deleting the task after it expires. A TimeSpan value - /// of 1 second indicates the task is set to delete when done. A value of TimeSpan.Zero indicates that the task should not be deleted. - /// - /// - /// A task expires after the end boundary has been exceeded for all triggers associated with the task. The end boundary for a - /// trigger is specified by the EndBoundary property of all trigger types. - /// - [DefaultValue(typeof(TimeSpan), "12:00:00")] - public TimeSpan DeleteExpiredTaskAfter - { - get - { - if (v2Settings != null) - return v2Settings.DeleteExpiredTaskAfter == "PT0S" ? TimeSpan.FromSeconds(1) : Task.StringToTimeSpan(v2Settings.DeleteExpiredTaskAfter); - return v1Task.HasFlags(TaskFlags.DeleteWhenDone) ? TimeSpan.FromSeconds(1) : TimeSpan.Zero; - } - set - { - if (v2Settings != null) - v2Settings.DeleteExpiredTaskAfter = value == TimeSpan.FromSeconds(1) ? "PT0S" : Task.TimeSpanToString(value); - else - v1Task.SetFlags(TaskFlags.DeleteWhenDone, value >= TimeSpan.FromSeconds(1)); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the task will not be started if the computer is running on battery power. - /// - [DefaultValue(true)] - public bool DisallowStartIfOnBatteries - { - get => v2Settings?.DisallowStartIfOnBatteries ?? v1Task.HasFlags(TaskFlags.DontStartIfOnBatteries); - set - { - if (v2Settings != null) - v2Settings.DisallowStartIfOnBatteries = value; - else - v1Task.SetFlags(TaskFlags.DontStartIfOnBatteries, value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the task will not be started if the task is triggered to run in a Remote - /// Applications Integrated Locally (RAIL) session. - /// - /// Property set for a task on a Task Scheduler version prior to 2.1. - [DefaultValue(false)] - [XmlIgnore] - public bool DisallowStartOnRemoteAppSession - { - get - { - if (v2Settings2 != null) - return v2Settings2.DisallowStartOnRemoteAppSession; - if (v2Settings3 != null) - return v2Settings3.DisallowStartOnRemoteAppSession; - return false; - } - set - { - if (v2Settings2 != null) - v2Settings2.DisallowStartOnRemoteAppSession = value; - else if (v2Settings3 != null) - v2Settings3.DisallowStartOnRemoteAppSession = value; - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_1); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the task is enabled. The task can be performed only when this setting is TRUE. - /// - [DefaultValue(true)] - public bool Enabled - { - get => v2Settings?.Enabled ?? !v1Task.HasFlags(TaskFlags.Disabled); - set - { - if (v2Settings != null) - v2Settings.Enabled = value; - else - v1Task.SetFlags(TaskFlags.Disabled, !value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the amount of time that is allowed to complete the task. By default, a task will be stopped 72 hours after it - /// starts to run. - /// - /// - /// The amount of time that is allowed to complete the task. When this parameter is set to , the - /// execution time limit is infinite. - /// - /// - /// If a task is started on demand, the ExecutionTimeLimit setting is bypassed. Therefore, a task that is started on demand will not - /// be terminated if it exceeds the ExecutionTimeLimit. - /// - [DefaultValue(typeof(TimeSpan), "3")] - public TimeSpan ExecutionTimeLimit - { - get - { - if (v2Settings != null) - return Task.StringToTimeSpan(v2Settings.ExecutionTimeLimit); - var ms = v1Task.GetMaxRunTime(); - return ms == InfiniteRunTimeV1 ? TimeSpan.Zero : TimeSpan.FromMilliseconds(ms); - } - set - { - if (v2Settings != null) - v2Settings.ExecutionTimeLimit = value == TimeSpan.Zero ? "PT0S" : Task.TimeSpanToString(value); - else - { - // Due to an issue introduced in Vista, and propagated to Windows 7, setting the MaxRunTime to INFINITE results in the - // task only running for 72 hours. For these operating systems, setting the RunTime to "INFINITE - 1" gets the desired - // behavior of allowing an "infinite" run of the task. - var ms = value == TimeSpan.Zero ? InfiniteRunTimeV1 : Convert.ToUInt32(value.TotalMilliseconds); - v1Task.SetMaxRunTime(ms); - if (value == TimeSpan.Zero && v1Task.GetMaxRunTime() != InfiniteRunTimeV1) - v1Task.SetMaxRunTime(InfiniteRunTimeV1 - 1); - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the task will not be visible in the UI by default. - [DefaultValue(false)] - public bool Hidden - { - get => v2Settings?.Hidden ?? v1Task.HasFlags(TaskFlags.Hidden); - set - { - if (v2Settings != null) - v2Settings.Hidden = value; - else - v1Task.SetFlags(TaskFlags.Hidden, value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the information that the Task Scheduler uses during Automatic maintenance. - [XmlIgnore] - [NotNull] - public MaintenanceSettings MaintenanceSettings => maintenanceSettings ??= new MaintenanceSettings(v2Settings3); - - /// Gets or sets the policy that defines how the Task Scheduler handles multiple instances of the task. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TaskInstancesPolicy), "IgnoreNew")] - [XmlIgnore] - public TaskInstancesPolicy MultipleInstances - { - get => v2Settings?.MultipleInstances ?? TaskInstancesPolicy.IgnoreNew; - set - { - if (v2Settings != null) - v2Settings.MultipleInstances = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the priority level of the task. - /// The priority. - /// Value set to AboveNormal or BelowNormal on Task Scheduler 1.0. - [DefaultValue(typeof(ProcessPriorityClass), "Normal")] - public ProcessPriorityClass Priority - { - get => v2Settings != null ? GetPriorityFromInt(v2Settings.Priority) : (ProcessPriorityClass)v1Task.GetPriority(); - set - { - if (v2Settings != null) - { - v2Settings.Priority = GetPriorityAsInt(value); - } - else - { - if (value == ProcessPriorityClass.AboveNormal || value == ProcessPriorityClass.BelowNormal) - throw new NotV1SupportedException("Unsupported priority level on Task Scheduler 1.0."); - v1Task.SetPriority((uint)value); - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the number of times that the Task Scheduler will attempt to restart the task. - /// - /// The number of times that the Task Scheduler will attempt to restart the task. If this property is set, the property must also be set. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(0)] - [XmlIgnore] - public int RestartCount - { - get => v2Settings?.RestartCount ?? 0; - set - { - if (v2Settings != null) - v2Settings.RestartCount = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a value that specifies how long the Task Scheduler will attempt to restart the task. - /// - /// A value that specifies how long the Task Scheduler will attempt to restart the task. If this property is set, the property must also be set. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan RestartInterval - { - get => v2Settings != null ? Task.StringToTimeSpan(v2Settings.RestartInterval) : TimeSpan.Zero; - set - { - if (v2Settings != null) - v2Settings.RestartInterval = Task.TimeSpanToString(value); - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the Task Scheduler will run the task only if the computer is in an idle condition. - /// - [DefaultValue(false)] - public bool RunOnlyIfIdle - { - get => v2Settings?.RunOnlyIfIdle ?? v1Task.HasFlags(TaskFlags.StartOnlyIfIdle); - set - { - if (v2Settings != null) - v2Settings.RunOnlyIfIdle = value; - else - v1Task.SetFlags(TaskFlags.StartOnlyIfIdle, value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the Task Scheduler will run the task only if the user is logged on (v1.0 only) - /// - /// Property set for a task on a Task Scheduler version other than 1.0. - [XmlIgnore] - public bool RunOnlyIfLoggedOn - { - get => v2Settings != null || v1Task.HasFlags(TaskFlags.RunOnlyIfLoggedOn); - set - { - if (v1Task != null) - v1Task.SetFlags(TaskFlags.RunOnlyIfLoggedOn, value); - else if (v2Settings != null) - throw new NotV2SupportedException("Task Scheduler 2.0 (1.2) does not support setting this property. You must use an InteractiveToken in order to have the task run in the current user session."); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the Task Scheduler will run the task only when a network is available. - [DefaultValue(false)] - public bool RunOnlyIfNetworkAvailable - { - get => v2Settings?.RunOnlyIfNetworkAvailable ?? v1Task.HasFlags(TaskFlags.RunIfConnectedToInternet); - set - { - if (v2Settings != null) - v2Settings.RunOnlyIfNetworkAvailable = value; - else - v1Task.SetFlags(TaskFlags.RunIfConnectedToInternet, value); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the Task Scheduler can start the task at any time after its scheduled time has passed. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(false)] - [XmlIgnore] - public bool StartWhenAvailable - { - get => v2Settings != null && v2Settings.StartWhenAvailable; - set - { - if (v2Settings != null) - v2Settings.StartWhenAvailable = value; - else - throw new NotV1SupportedException(); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the task will be stopped if the computer switches to battery power. - [DefaultValue(true)] - public bool StopIfGoingOnBatteries - { - get => v2Settings?.StopIfGoingOnBatteries ?? v1Task.HasFlags(TaskFlags.KillIfGoingOnBatteries); - set - { - if (v2Settings != null) - v2Settings.StopIfGoingOnBatteries = value; - else - v1Task.SetFlags(TaskFlags.KillIfGoingOnBatteries, value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the Unified Scheduling Engine will be utilized to run this task. - /// Property set for a task on a Task Scheduler version prior to 2.1. - [DefaultValue(false)] - [XmlIgnore] - public bool UseUnifiedSchedulingEngine - { - get - { - if (v2Settings2 != null) - return v2Settings2.UseUnifiedSchedulingEngine; - if (v2Settings3 != null) - return v2Settings3.UseUnifiedSchedulingEngine; - return false; - } - set - { - if (v2Settings2 != null) - v2Settings2.UseUnifiedSchedulingEngine = value; - else if (v2Settings3 != null) - v2Settings3.UseUnifiedSchedulingEngine = value; - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_1); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a boolean value that indicates whether the task is automatically disabled every time Windows starts. - /// Property set for a task on a Task Scheduler version prior to 2.2. - [DefaultValue(false)] - [XmlIgnore] - public bool Volatile - { - get => v2Settings3 != null && v2Settings3.Volatile; - set - { - if (v2Settings3 != null) - v2Settings3.Volatile = value; - else - throw new NotSupportedPriorToException(TaskCompatibility.V2_2); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates that the Task Scheduler will wake the computer when it is time to run the task. - /// - [DefaultValue(false)] - public bool WakeToRun - { - get => v2Settings?.WakeToRun ?? v1Task.HasFlags(TaskFlags.SystemRequired); - set - { - if (v2Settings != null) - v2Settings.WakeToRun = value; - else - v1Task.SetFlags(TaskFlags.SystemRequired, value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets an XML-formatted definition of the task settings. - [XmlIgnore] - public string XmlText - { - get => v2Settings != null ? v2Settings.XmlText : XmlSerializationHelper.WriteObjectToXmlText(this); - set - { - if (v2Settings != null) - v2Settings.XmlText = value; - else - XmlSerializationHelper.ReadObjectFromXmlText(value, this); - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the information that specifies how the Task Scheduler performs tasks when the computer is in an idle state. - /// - [NotNull] - public IdleSettings IdleSettings => idleSettings ??= v2Settings != null ? new IdleSettings(v2Settings.IdleSettings) : new IdleSettings(v1Task); - - /// - /// Gets or sets the network settings object that contains a network profile identifier and name. If the RunOnlyIfNetworkAvailable - /// property of ITaskSettings is true and a network profile is specified in the NetworkSettings property, then the task will run - /// only if the specified network profile is available. - /// - [XmlIgnore] - [NotNull] - public NetworkSettings NetworkSettings => networkSettings ??= new NetworkSettings(v2Settings?.NetworkSettings); - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Settings != null) - Marshal.ReleaseComObject(v2Settings); - idleSettings = null; - networkSettings = null; - v1Task = null; - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() - { - if (v2Settings != null || v1Task != null) - return DebugHelper.GetDebugString(this); - return base.ToString(); - } - - XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(XmlReader reader) - { - if (!reader.IsEmptyElement) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); - XmlSerializationHelper.ReadObjectProperties(reader, this, ConvertXmlProperty); - reader.ReadEndElement(); - } - else - reader.Skip(); - } - - void IXmlSerializable.WriteXml(XmlWriter writer) => XmlSerializationHelper.WriteObjectProperties(writer, this, ConvertXmlProperty); - - private bool ConvertXmlProperty(PropertyInfo pi, object obj, ref object value) - { - if (pi.Name == "Priority" && value != null) - { - if (value is int) - value = GetPriorityFromInt((int)value); - else if (value is ProcessPriorityClass) - value = GetPriorityAsInt((ProcessPriorityClass)value); - return true; - } - return false; - } - - private int GetPriorityAsInt(ProcessPriorityClass value) - { - // Check for back-door case where exact value is being passed and cast to ProcessPriorityClass - if ((int)value <= 10 && value >= 0) return (int)value; - var p = 7; - switch (value) - { - case ProcessPriorityClass.AboveNormal: - p = 3; - break; - - case ProcessPriorityClass.High: - p = 1; - break; - - case ProcessPriorityClass.Idle: - p = 10; - break; - - case ProcessPriorityClass.Normal: - p = 5; - break; - - case ProcessPriorityClass.RealTime: - p = 0; - break; - // case ProcessPriorityClass.BelowNormal: default: break; - } - return p; - } - - private ProcessPriorityClass GetPriorityFromInt(int value) - { - switch (value) - { - case 0: - return ProcessPriorityClass.RealTime; - - case 1: - return ProcessPriorityClass.High; - - case 2: - case 3: - return ProcessPriorityClass.AboveNormal; - - case 4: - case 5: - case 6: - return ProcessPriorityClass.Normal; - // case 7: case 8: - default: - return ProcessPriorityClass.BelowNormal; - - case 9: - case 10: - return ProcessPriorityClass.Idle; - } - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - internal static class DebugHelper - { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Language", "CSE0003:Use expression-bodied members", Justification = "")] - public static string GetDebugString(object inst) - { -#if DEBUG - var sb = new StringBuilder(); - foreach (var pi in inst.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) - { - if (pi.Name.StartsWith("Xml")) - continue; - var outval = pi.GetValue(inst, null); - if (outval != null) - { - var defval = XmlSerializationHelper.GetDefaultValue(pi); - if (!outval.Equals(defval)) - { - var s = $"{pi.Name}:{outval}"; - if (s.Length > 30) s = s.Remove(30); - sb.Append(s + "; "); - } + var defval = XmlSerializationHelper.GetDefaultValue(pi); + if (!outval.Equals(defval)) + { + var s = $"{pi.Name}:{outval}"; + if (s.Length > 30) s = s.Remove(30); + sb.Append(s + "; "); + } } } return sb.ToString(); #else - return inst.GetType().ToString(); + return inst.GetType().ToString(); #endif - } - } - - internal static class TSInteropExt - { - public static string GetDataItem(this ITask v1Task, string name) - { - TaskDefinition.GetV1TaskDataDictionary(v1Task).TryGetValue(name, out var ret); - return ret; - } - - public static bool HasFlags(this ITask v1Task, TaskFlags flags) => v1Task.GetFlags().IsFlagSet(flags); - - public static void SetDataItem(this ITask v1Task, string name, string value) - { - var d = TaskDefinition.GetV1TaskDataDictionary(v1Task); - d[name] = value; - TaskDefinition.SetV1TaskData(v1Task, d); - } - - public static void SetFlags(this ITask v1Task, TaskFlags flags, bool value = true) => v1Task.SetFlags(v1Task.GetFlags().SetFlags(flags, value)); - } - - internal class DefaultValueExAttribute : DefaultValueAttribute - { - public DefaultValueExAttribute(Type type, string value) : base(null) - { - try - { - if (type == typeof(Version)) - { - SetValue(new Version(value)); - return; - } - SetValue(TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value)); - } - catch - { - Debug.Fail("Default value attribute of type " + type.FullName + " threw converting from the string '" + value + "'."); - } - } - } + } + } + + internal static class TSInteropExt + { + public static string GetDataItem(this ITask v1Task, string name) + { + TaskDefinition.GetV1TaskDataDictionary(v1Task).TryGetValue(name, out var ret); + return ret; + } + + public static bool HasFlags(this ITask v1Task, TaskFlags flags) => v1Task.GetFlags().IsFlagSet(flags); + + public static void SetDataItem(this ITask v1Task, string name, string value) + { + var d = TaskDefinition.GetV1TaskDataDictionary(v1Task); + d[name] = value; + TaskDefinition.SetV1TaskData(v1Task, d); + } + + public static void SetFlags(this ITask v1Task, TaskFlags flags, bool value = true) => v1Task.SetFlags(v1Task.GetFlags().SetFlags(flags, value)); + } + + internal class DefaultValueExAttribute : DefaultValueAttribute + { + public DefaultValueExAttribute(Type type, string value) : base(null) + { + try + { + if (type == typeof(Version)) + { + SetValue(new Version(value)); + return; + } + SetValue(TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value)); + } + catch + { + Debug.Fail("Default value attribute of type " + type.FullName + " threw converting from the string '" + value + "'."); + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskCollection.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskCollection.cs index c6f4bc0a0..0a599a9b4 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskCollection.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskCollection.cs @@ -9,395 +9,395 @@ namespace winPEAS.TaskScheduler { - /// - /// Collection of running tasks in a . This class has no public constructor and can only be accessed via the - /// properties and functions within . - /// - public sealed class RunningTaskCollection : IReadOnlyList, IDisposable - { - private readonly TaskService svc; - private readonly IRunningTaskCollection v2Coll; - - internal RunningTaskCollection([NotNull] TaskService svc) => this.svc = svc; - - internal RunningTaskCollection([NotNull] TaskService svc, [NotNull] IRunningTaskCollection iTaskColl) - { - this.svc = svc; - v2Coll = iTaskColl; - } - - /// Gets the number of registered tasks in the collection. - public int Count - { - get - { - if (v2Coll != null) - return v2Coll.Count; - var i = 0; - var v1Enum = new V1RunningTaskEnumerator(svc); - while (v1Enum.MoveNext()) - i++; - return i; - } - } - - /// Gets the specified running task from the collection. - /// The index of the running task to be retrieved. - /// A instance. - public RunningTask this[int index] - { - get - { - if (v2Coll != null) - { - var irt = v2Coll[++index]; - return new RunningTask(svc, TaskService.GetTask(svc.v2TaskService, irt.Path), irt); - } - - var i = 0; - var v1Enum = new V1RunningTaskEnumerator(svc); - while (v1Enum.MoveNext()) - if (i++ == index) - return v1Enum.Current; - throw new ArgumentOutOfRangeException(nameof(index)); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Coll != null) - Marshal.ReleaseComObject(v2Coll); - } - - /// Gets an IEnumerator instance for this collection. - /// An enumerator. - public IEnumerator GetEnumerator() - { - if (v2Coll != null) - return new ComEnumerator(() => v2Coll.Count, (object o) => v2Coll[o], o => - { - IRegisteredTask task = null; - try { task = TaskService.GetTask(svc.v2TaskService, o.Path); } catch { } - return task == null ? null : new RunningTask(svc, task, o); - }); - return new V1RunningTaskEnumerator(svc); - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() => $"RunningTaskCollection; Count: {Count}"; - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - - private class V1RunningTaskEnumerator : IEnumerator - { - private readonly TaskService svc; - private readonly TaskCollection.V1TaskEnumerator tEnum; - - internal V1RunningTaskEnumerator([NotNull] TaskService svc) - { - this.svc = svc; - tEnum = new TaskCollection.V1TaskEnumerator(svc); - } - - public RunningTask Current => new RunningTask(svc, tEnum.ICurrent); - - object System.Collections.IEnumerator.Current => Current; - - /// Releases all resources used by this class. - public void Dispose() => tEnum.Dispose(); - - public bool MoveNext() => tEnum.MoveNext() && (tEnum.Current?.State == TaskState.Running || MoveNext()); - - public void Reset() => tEnum.Reset(); - } - } - - /// - /// Contains all the tasks that are registered within a . This class has no public constructor and can only be - /// accessed via the properties and functions within . - /// - /// - /// Potentially breaking change in 1.6.2 and later where under V1 the list previously included the '.job' extension on the task name. - /// This has been removed so that it is consistent with V2. - /// - /// - /// public class Program - /// { - /// bool RootFolderHasTask(string taskName) - /// { - /// if (TaskService.Instance.RootFolder.Tasks.Count > 0) - /// { - /// return TaskService.Instance.RootFolder.Tasks.Exists(taskName); - /// } - /// return false; - /// } - /// - /// TaskCollection GetRootTasksStartingWith(string value) - /// { - /// var pattern = $"^{Regex.Escape(value)}.*$"; - /// return TaskService.Instance.RootFolder.GetTasks(new Regex(pattern)); - /// } - /// - /// public static void Main() - /// { - /// foreach (var task in GetRootTasksStartingWith("MyCo")) - /// if (RootFolderHasTask(task.Name)) - /// Console.WriteLine(task.Name); - /// } - /// } - /// - [PublicAPI] - public sealed class TaskCollection : IReadOnlyList, IDisposable - { - private readonly TaskFolder fld; - private readonly TaskService svc; - private readonly IRegisteredTaskCollection v2Coll; - private Regex filter; - private ITaskScheduler v1TS; - - internal TaskCollection([NotNull] TaskService svc, Regex filter = null) - { - this.svc = svc; - Filter = filter; - v1TS = svc.v1TaskScheduler; - } - - internal TaskCollection([NotNull] TaskFolder folder, [NotNull] IRegisteredTaskCollection iTaskColl, Regex filter = null) - { - svc = folder.TaskService; - Filter = filter; - fld = folder; - v2Coll = iTaskColl; - } - - /// Gets the number of registered tasks in the collection. - public int Count - { - get - { - var i = 0; - if (v2Coll != null) - { - var v2Enum = new V2TaskEnumerator(fld, v2Coll, filter); - while (v2Enum.MoveNext()) - i++; - } - else - { - var v1Enum = new V1TaskEnumerator(svc, filter); - return v1Enum.Count; - } - return i; - } - } - - /// Gets or sets the regular expression filter for task names. - /// The regular expression filter. - private Regex Filter - { - get => filter; - set - { - var sfilter = value?.ToString().TrimStart('^').TrimEnd('$') ?? string.Empty; - if (sfilter == string.Empty || sfilter == "*") - filter = null; - else - { - if (value != null && value.ToString().TrimEnd('$').EndsWith("\\.job", StringComparison.InvariantCultureIgnoreCase)) - filter = new Regex(value.ToString().Replace("\\.job", "")); - else - filter = value; - } - } - } - - /// Gets the specified registered task from the collection. - /// The index of the registered task to be retrieved. - /// A instance that contains the requested context. - public Task this[int index] - { - get - { - var i = 0; - var te = GetEnumerator(); - while (te.MoveNext()) - if (i++ == index) - return te.Current; - throw new ArgumentOutOfRangeException(nameof(index)); - } - } - - /// Gets the named registered task from the collection. - /// The name of the registered task to be retrieved. - /// A instance that contains the requested context. - public Task this[string name] - { - get - { - if (v2Coll != null) - return Task.CreateTask(svc, v2Coll[name]); - - var v1Task = svc.GetTask(name); - if (v1Task != null) - return v1Task; - - throw new ArgumentOutOfRangeException(nameof(name)); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - v1TS = null; - if (v2Coll != null) - Marshal.ReleaseComObject(v2Coll); - } - - /// Determines whether the specified task exists. - /// The name of the task. - /// true if task exists; otherwise, false. - public bool Exists([NotNull] string taskName) - { - try - { - if (v2Coll != null) - return v2Coll[taskName] != null; - - return svc.GetTask(taskName) != null; - } - catch { } - return false; - } - - /// Gets the collection enumerator for the register task collection. - /// An for this collection. - public IEnumerator GetEnumerator() - { - if (v1TS != null) - return new V1TaskEnumerator(svc, filter); - return new V2TaskEnumerator(fld, v2Coll, filter); - } - - /// Returns a that represents this instance. - /// A that represents this instance. - public override string ToString() => $"TaskCollection; Count: {Count}"; - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - - internal class V1TaskEnumerator : IEnumerator - { - private readonly Regex filter; - private readonly TaskService svc; - private readonly IEnumWorkItems wienum; - private string curItem; - private ITaskScheduler ts; - - /// Internal constructor - /// TaskService instance - /// The filter. - internal V1TaskEnumerator(TaskService svc, Regex filter = null) - { - this.svc = svc; - this.filter = filter; - ts = svc.v1TaskScheduler; - wienum = ts?.Enum(); - Reset(); - } - - /// Retrieves the current task. See for more information. - public Task Current => new Task(svc, ICurrent); - - object System.Collections.IEnumerator.Current => Current; - - internal int Count - { - get - { - var i = 0; - Reset(); - while (MoveNext()) - i++; - Reset(); - return i; - } - } - - internal ITask ICurrent => TaskService.GetTask(ts, curItem); - - /// Releases all resources used by this class. - public void Dispose() - { - if (wienum != null) Marshal.ReleaseComObject(wienum); - ts = null; - } - - /// Moves to the next task. See MoveNext for more information. - /// true if next task found, false if no more tasks. - public bool MoveNext() - { - var names = IntPtr.Zero; - var valid = false; - do - { - curItem = null; - uint uFetched = 0; - try - { - wienum?.Next(1, out names, out uFetched); - if (uFetched != 1) - break; - using (var name = new CoTaskMemString(Marshal.ReadIntPtr(names))) - curItem = name.ToString(); - if (curItem != null && curItem.EndsWith(".job", StringComparison.InvariantCultureIgnoreCase)) - curItem = curItem.Remove(curItem.Length - 4); - } - catch { } - finally { Marshal.FreeCoTaskMem(names); names = IntPtr.Zero; } - - // If name doesn't match filter, look for next item - if (filter != null && curItem != null) - { - if (!filter.IsMatch(curItem)) - continue; - } - - ITask itask = null; - try { itask = ICurrent; valid = true; } - catch { valid = false; } - finally { Marshal.ReleaseComObject(itask); } - } while (!valid); - - return (curItem != null); - } - - /// Reset task enumeration. See Reset for more information. - public void Reset() - { - curItem = null; - wienum?.Reset(); - } - } - - private class V2TaskEnumerator : ComEnumerator - { - private readonly Regex filter; - - internal V2TaskEnumerator(TaskFolder folder, IRegisteredTaskCollection iTaskColl, Regex filter = null) : - base(() => iTaskColl.Count, (object o) => iTaskColl[o], o => Task.CreateTask(folder.TaskService, o)) => this.filter = filter; - - public override bool MoveNext() - { - var hasNext = base.MoveNext(); - while (hasNext) - { - if (filter == null || filter.IsMatch(iEnum?.Current?.Name ?? "")) - break; - hasNext = base.MoveNext(); - } - return hasNext; - } - } - } + /// + /// Collection of running tasks in a . This class has no public constructor and can only be accessed via the + /// properties and functions within . + /// + public sealed class RunningTaskCollection : IReadOnlyList, IDisposable + { + private readonly TaskService svc; + private readonly IRunningTaskCollection v2Coll; + + internal RunningTaskCollection([NotNull] TaskService svc) => this.svc = svc; + + internal RunningTaskCollection([NotNull] TaskService svc, [NotNull] IRunningTaskCollection iTaskColl) + { + this.svc = svc; + v2Coll = iTaskColl; + } + + /// Gets the number of registered tasks in the collection. + public int Count + { + get + { + if (v2Coll != null) + return v2Coll.Count; + var i = 0; + var v1Enum = new V1RunningTaskEnumerator(svc); + while (v1Enum.MoveNext()) + i++; + return i; + } + } + + /// Gets the specified running task from the collection. + /// The index of the running task to be retrieved. + /// A instance. + public RunningTask this[int index] + { + get + { + if (v2Coll != null) + { + var irt = v2Coll[++index]; + return new RunningTask(svc, TaskService.GetTask(svc.v2TaskService, irt.Path), irt); + } + + var i = 0; + var v1Enum = new V1RunningTaskEnumerator(svc); + while (v1Enum.MoveNext()) + if (i++ == index) + return v1Enum.Current; + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Coll != null) + Marshal.ReleaseComObject(v2Coll); + } + + /// Gets an IEnumerator instance for this collection. + /// An enumerator. + public IEnumerator GetEnumerator() + { + if (v2Coll != null) + return new ComEnumerator(() => v2Coll.Count, (object o) => v2Coll[o], o => + { + IRegisteredTask task = null; + try { task = TaskService.GetTask(svc.v2TaskService, o.Path); } catch { } + return task == null ? null : new RunningTask(svc, task, o); + }); + return new V1RunningTaskEnumerator(svc); + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() => $"RunningTaskCollection; Count: {Count}"; + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + + private class V1RunningTaskEnumerator : IEnumerator + { + private readonly TaskService svc; + private readonly TaskCollection.V1TaskEnumerator tEnum; + + internal V1RunningTaskEnumerator([NotNull] TaskService svc) + { + this.svc = svc; + tEnum = new TaskCollection.V1TaskEnumerator(svc); + } + + public RunningTask Current => new RunningTask(svc, tEnum.ICurrent); + + object System.Collections.IEnumerator.Current => Current; + + /// Releases all resources used by this class. + public void Dispose() => tEnum.Dispose(); + + public bool MoveNext() => tEnum.MoveNext() && (tEnum.Current?.State == TaskState.Running || MoveNext()); + + public void Reset() => tEnum.Reset(); + } + } + + /// + /// Contains all the tasks that are registered within a . This class has no public constructor and can only be + /// accessed via the properties and functions within . + /// + /// + /// Potentially breaking change in 1.6.2 and later where under V1 the list previously included the '.job' extension on the task name. + /// This has been removed so that it is consistent with V2. + /// + /// + /// public class Program + /// { + /// bool RootFolderHasTask(string taskName) + /// { + /// if (TaskService.Instance.RootFolder.Tasks.Count > 0) + /// { + /// return TaskService.Instance.RootFolder.Tasks.Exists(taskName); + /// } + /// return false; + /// } + /// + /// TaskCollection GetRootTasksStartingWith(string value) + /// { + /// var pattern = $"^{Regex.Escape(value)}.*$"; + /// return TaskService.Instance.RootFolder.GetTasks(new Regex(pattern)); + /// } + /// + /// public static void Main() + /// { + /// foreach (var task in GetRootTasksStartingWith("MyCo")) + /// if (RootFolderHasTask(task.Name)) + /// Console.WriteLine(task.Name); + /// } + /// } + /// + [PublicAPI] + public sealed class TaskCollection : IReadOnlyList, IDisposable + { + private readonly TaskFolder fld; + private readonly TaskService svc; + private readonly IRegisteredTaskCollection v2Coll; + private Regex filter; + private ITaskScheduler v1TS; + + internal TaskCollection([NotNull] TaskService svc, Regex filter = null) + { + this.svc = svc; + Filter = filter; + v1TS = svc.v1TaskScheduler; + } + + internal TaskCollection([NotNull] TaskFolder folder, [NotNull] IRegisteredTaskCollection iTaskColl, Regex filter = null) + { + svc = folder.TaskService; + Filter = filter; + fld = folder; + v2Coll = iTaskColl; + } + + /// Gets the number of registered tasks in the collection. + public int Count + { + get + { + var i = 0; + if (v2Coll != null) + { + var v2Enum = new V2TaskEnumerator(fld, v2Coll, filter); + while (v2Enum.MoveNext()) + i++; + } + else + { + var v1Enum = new V1TaskEnumerator(svc, filter); + return v1Enum.Count; + } + return i; + } + } + + /// Gets or sets the regular expression filter for task names. + /// The regular expression filter. + private Regex Filter + { + get => filter; + set + { + var sfilter = value?.ToString().TrimStart('^').TrimEnd('$') ?? string.Empty; + if (sfilter == string.Empty || sfilter == "*") + filter = null; + else + { + if (value != null && value.ToString().TrimEnd('$').EndsWith("\\.job", StringComparison.InvariantCultureIgnoreCase)) + filter = new Regex(value.ToString().Replace("\\.job", "")); + else + filter = value; + } + } + } + + /// Gets the specified registered task from the collection. + /// The index of the registered task to be retrieved. + /// A instance that contains the requested context. + public Task this[int index] + { + get + { + var i = 0; + var te = GetEnumerator(); + while (te.MoveNext()) + if (i++ == index) + return te.Current; + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + + /// Gets the named registered task from the collection. + /// The name of the registered task to be retrieved. + /// A instance that contains the requested context. + public Task this[string name] + { + get + { + if (v2Coll != null) + return Task.CreateTask(svc, v2Coll[name]); + + var v1Task = svc.GetTask(name); + if (v1Task != null) + return v1Task; + + throw new ArgumentOutOfRangeException(nameof(name)); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + v1TS = null; + if (v2Coll != null) + Marshal.ReleaseComObject(v2Coll); + } + + /// Determines whether the specified task exists. + /// The name of the task. + /// true if task exists; otherwise, false. + public bool Exists([NotNull] string taskName) + { + try + { + if (v2Coll != null) + return v2Coll[taskName] != null; + + return svc.GetTask(taskName) != null; + } + catch { } + return false; + } + + /// Gets the collection enumerator for the register task collection. + /// An for this collection. + public IEnumerator GetEnumerator() + { + if (v1TS != null) + return new V1TaskEnumerator(svc, filter); + return new V2TaskEnumerator(fld, v2Coll, filter); + } + + /// Returns a that represents this instance. + /// A that represents this instance. + public override string ToString() => $"TaskCollection; Count: {Count}"; + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + + internal class V1TaskEnumerator : IEnumerator + { + private readonly Regex filter; + private readonly TaskService svc; + private readonly IEnumWorkItems wienum; + private string curItem; + private ITaskScheduler ts; + + /// Internal constructor + /// TaskService instance + /// The filter. + internal V1TaskEnumerator(TaskService svc, Regex filter = null) + { + this.svc = svc; + this.filter = filter; + ts = svc.v1TaskScheduler; + wienum = ts?.Enum(); + Reset(); + } + + /// Retrieves the current task. See for more information. + public Task Current => new Task(svc, ICurrent); + + object System.Collections.IEnumerator.Current => Current; + + internal int Count + { + get + { + var i = 0; + Reset(); + while (MoveNext()) + i++; + Reset(); + return i; + } + } + + internal ITask ICurrent => TaskService.GetTask(ts, curItem); + + /// Releases all resources used by this class. + public void Dispose() + { + if (wienum != null) Marshal.ReleaseComObject(wienum); + ts = null; + } + + /// Moves to the next task. See MoveNext for more information. + /// true if next task found, false if no more tasks. + public bool MoveNext() + { + var names = IntPtr.Zero; + var valid = false; + do + { + curItem = null; + uint uFetched = 0; + try + { + wienum?.Next(1, out names, out uFetched); + if (uFetched != 1) + break; + using (var name = new CoTaskMemString(Marshal.ReadIntPtr(names))) + curItem = name.ToString(); + if (curItem != null && curItem.EndsWith(".job", StringComparison.InvariantCultureIgnoreCase)) + curItem = curItem.Remove(curItem.Length - 4); + } + catch { } + finally { Marshal.FreeCoTaskMem(names); names = IntPtr.Zero; } + + // If name doesn't match filter, look for next item + if (filter != null && curItem != null) + { + if (!filter.IsMatch(curItem)) + continue; + } + + ITask itask = null; + try { itask = ICurrent; valid = true; } + catch { valid = false; } + finally { Marshal.ReleaseComObject(itask); } + } while (!valid); + + return (curItem != null); + } + + /// Reset task enumeration. See Reset for more information. + public void Reset() + { + curItem = null; + wienum?.Reset(); + } + } + + private class V2TaskEnumerator : ComEnumerator + { + private readonly Regex filter; + + internal V2TaskEnumerator(TaskFolder folder, IRegisteredTaskCollection iTaskColl, Regex filter = null) : + base(() => iTaskColl.Count, (object o) => iTaskColl[o], o => Task.CreateTask(folder.TaskService, o)) => this.filter = filter; + + public override bool MoveNext() + { + var hasNext = base.MoveNext(); + while (hasNext) + { + if (filter == null || filter.IsMatch(iEnum?.Current?.Name ?? "")) + break; + hasNext = base.MoveNext(); + } + return hasNext; + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/InteropUtil.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/InteropUtil.cs index a9869ab05..f9485c898 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/InteropUtil.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/InteropUtil.cs @@ -1,147 +1,144 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler.TaskEditor.Native { - internal static class InteropUtil - { - private const int cbBuffer = 256; - - public static T ToStructure(IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T)); - - public static IntPtr StructureToPtr(object value) - { - IntPtr ret = Marshal.AllocHGlobal(Marshal.SizeOf(value)); - Marshal.StructureToPtr(value, ret, false); - return ret; - } - - public static void AllocString(ref IntPtr ptr, ref uint size) - { - FreeString(ref ptr, ref size); - if (size == 0) size = cbBuffer; - ptr = Marshal.AllocHGlobal(cbBuffer); - } - - public static void FreeString(ref IntPtr ptr, ref uint size) - { - if (ptr != IntPtr.Zero) - { - Marshal.FreeHGlobal(ptr); - ptr = IntPtr.Zero; - size = 0; - } - } - - public static string GetString(IntPtr pString) => Marshal.PtrToStringUni(pString); - - public static bool SetString(ref IntPtr ptr, ref uint size, string value = null) - { - string s = GetString(ptr); - if (value == string.Empty) value = null; - if (string.CompareOrdinal(s, value) != 0) - { - FreeString(ref ptr, ref size); - if (value != null) - { - ptr = Marshal.StringToHGlobalUni(value); - size = (uint)value.Length + 1; - } - return true; - } - return false; - } - - /// - /// Converts an that points to a C-style array into a CLI array. - /// - /// Type of native structure used by the C-style array. - /// Output type for the CLI array. must be able to convert to . - /// The pointing to the native array. - /// The number of items in the native array. - /// An array of type containing the converted elements of the native array. - public static T[] ToArray(IntPtr ptr, int count) where TS : IConvertible - { - var ret = new T[count]; - var stSize = Marshal.SizeOf(typeof(TS)); - for (var i = 0; i < count; i++) - { - var tempPtr = new IntPtr(ptr.ToInt64() + (i * stSize)); - var val = ToStructure(tempPtr); - ret[i] = (T)Convert.ChangeType(val, typeof(T)); - } - return ret; - } - - /// - /// Converts an that points to a C-style array into a CLI array. - /// - /// Type of native structure used by the C-style array. - /// The pointing to the native array. - /// The number of items in the native array. - /// An array of type containing the elements of the native array. - public static T[] ToArray(IntPtr ptr, int count) - { - var ret = new T[count]; - var stSize = Marshal.SizeOf(typeof(T)); - for (var i = 0; i < count; i++) - { - var tempPtr = new IntPtr(ptr.ToInt64() + (i * stSize)); - ret[i] = ToStructure(tempPtr); - } - return ret; - } - } - - internal class ComEnumerator : IEnumerator where T : class where TIn : class - { - protected readonly Func converter; - protected IEnumerator iEnum; - - public ComEnumerator(Func getCount, Func indexer, Func converter) - { - IEnumerator Enumerate() - { - for (var x = 1; x <= getCount(); x++) - yield return indexer(x); - } - - this.converter = converter; - iEnum = Enumerate(); - } - - public ComEnumerator(Func getCount, Func indexer, Func converter) - { - IEnumerator Enumerate() - { - for (var x = 1; x <= getCount(); x++) - yield return indexer(x); - } - - this.converter = converter; - iEnum = Enumerate(); - } - - object IEnumerator.Current => Current; - - public virtual T Current => converter(iEnum?.Current); - - public virtual void Dispose() - { - iEnum?.Dispose(); - iEnum = null; - } - - public virtual bool MoveNext() => iEnum?.MoveNext() ?? false; - - public virtual void Reset() - { - iEnum?.Reset(); - } - } + internal static class InteropUtil + { + private const int cbBuffer = 256; + + public static T ToStructure(IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T)); + + public static IntPtr StructureToPtr(object value) + { + IntPtr ret = Marshal.AllocHGlobal(Marshal.SizeOf(value)); + Marshal.StructureToPtr(value, ret, false); + return ret; + } + + public static void AllocString(ref IntPtr ptr, ref uint size) + { + FreeString(ref ptr, ref size); + if (size == 0) size = cbBuffer; + ptr = Marshal.AllocHGlobal(cbBuffer); + } + + public static void FreeString(ref IntPtr ptr, ref uint size) + { + if (ptr != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptr); + ptr = IntPtr.Zero; + size = 0; + } + } + + public static string GetString(IntPtr pString) => Marshal.PtrToStringUni(pString); + + public static bool SetString(ref IntPtr ptr, ref uint size, string value = null) + { + string s = GetString(ptr); + if (value == string.Empty) value = null; + if (string.CompareOrdinal(s, value) != 0) + { + FreeString(ref ptr, ref size); + if (value != null) + { + ptr = Marshal.StringToHGlobalUni(value); + size = (uint)value.Length + 1; + } + return true; + } + return false; + } + + /// + /// Converts an that points to a C-style array into a CLI array. + /// + /// Type of native structure used by the C-style array. + /// Output type for the CLI array. must be able to convert to . + /// The pointing to the native array. + /// The number of items in the native array. + /// An array of type containing the converted elements of the native array. + public static T[] ToArray(IntPtr ptr, int count) where TS : IConvertible + { + var ret = new T[count]; + var stSize = Marshal.SizeOf(typeof(TS)); + for (var i = 0; i < count; i++) + { + var tempPtr = new IntPtr(ptr.ToInt64() + (i * stSize)); + var val = ToStructure(tempPtr); + ret[i] = (T)Convert.ChangeType(val, typeof(T)); + } + return ret; + } + + /// + /// Converts an that points to a C-style array into a CLI array. + /// + /// Type of native structure used by the C-style array. + /// The pointing to the native array. + /// The number of items in the native array. + /// An array of type containing the elements of the native array. + public static T[] ToArray(IntPtr ptr, int count) + { + var ret = new T[count]; + var stSize = Marshal.SizeOf(typeof(T)); + for (var i = 0; i < count; i++) + { + var tempPtr = new IntPtr(ptr.ToInt64() + (i * stSize)); + ret[i] = ToStructure(tempPtr); + } + return ret; + } + } + + internal class ComEnumerator : IEnumerator where T : class where TIn : class + { + protected readonly Func converter; + protected IEnumerator iEnum; + + public ComEnumerator(Func getCount, Func indexer, Func converter) + { + IEnumerator Enumerate() + { + for (var x = 1; x <= getCount(); x++) + yield return indexer(x); + } + + this.converter = converter; + iEnum = Enumerate(); + } + + public ComEnumerator(Func getCount, Func indexer, Func converter) + { + IEnumerator Enumerate() + { + for (var x = 1; x <= getCount(); x++) + yield return indexer(x); + } + + this.converter = converter; + iEnum = Enumerate(); + } + + object IEnumerator.Current => Current; + + public virtual T Current => converter(iEnum?.Current); + + public virtual void Dispose() + { + iEnum?.Dispose(); + iEnum = null; + } + + public virtual bool MoveNext() => iEnum?.MoveNext() ?? false; + + public virtual void Reset() + { + iEnum?.Reset(); + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NTDSAPI.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NTDSAPI.cs index ef72f3127..5228da08f 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NTDSAPI.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NTDSAPI.cs @@ -8,145 +8,145 @@ namespace winPEAS.TaskScheduler.TaskEditor.Native { internal static partial class NativeMethods - { - /// - /// Defines the errors returned by the status member of the DS_NAME_RESULT_ITEM structure. These are potential errors that may be encountered while a name is converted by the DsCrackNames function. - /// - public enum DS_NAME_ERROR : uint - { - /// The conversion was successful. - DS_NAME_NO_ERROR = 0, - - ///Generic processing error occurred. - DS_NAME_ERROR_RESOLVING = 1, - - ///The name cannot be found or the caller does not have permission to access the name. - DS_NAME_ERROR_NOT_FOUND = 2, - - ///The input name is mapped to more than one output name or the desired format did not have a single, unique value for the object found. - DS_NAME_ERROR_NOT_UNIQUE = 3, - - ///The input name was found, but the associated output format cannot be found. This can occur if the object does not have all the required attributes. - DS_NAME_ERROR_NO_MAPPING = 4, - - ///Unable to resolve entire name, but was able to determine in which domain object resides. The caller is expected to retry the call at a domain controller for the specified domain. The entire name cannot be resolved, but the domain that the object resides in could be determined. The pDomain member of the DS_NAME_RESULT_ITEM contains valid data when this error is specified. - DS_NAME_ERROR_DOMAIN_ONLY = 5, - - ///A syntactical mapping cannot be performed on the client without transmitting over the network. - DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 6, - - ///The name is from an external trusted forest. - DS_NAME_ERROR_TRUST_REFERRAL = 7 - } - - /// - /// Class that provides methods against a AD domain service. - /// - /// - [SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - public class DomainService : IDisposable - { - IntPtr handle = IntPtr.Zero; - - /// - /// Initializes a new instance of the class. - /// - /// Name of the domain controller. - /// Name of the DNS domain. - /// - public DomainService(string domainControllerName = null, string dnsDomainName = null) - { - Ntdsapi.DsBind(domainControllerName, dnsDomainName, out handle); - } - - /// - /// Converts a directory service object name from any format to the UPN. - /// - /// The name to convert. - /// The corresponding UPN. - /// Unable to resolve user name. - public string CrackName(string name) - { - var res = CrackNames(new string[] { name }); - if (res == null || res.Length == 0 || res[0].status != NativeMethods.DS_NAME_ERROR.DS_NAME_NO_ERROR) - throw new SecurityException("Unable to resolve user name."); - return res[0].pName; - } - - /// - /// Converts an array of directory service object names from one format to another. Name conversion enables client applications to map between the multiple names used to identify various directory service objects. - /// - /// The names to convert. - /// Values used to determine how the name syntax will be cracked. - /// Format of the input names. - /// Desired format for the output names. - /// An array of DS_NAME_RESULT_ITEM structures. Each element of this array represents a single converted name. - public DS_NAME_RESULT_ITEM[] CrackNames(string[] names = null, DS_NAME_FLAGS flags = DS_NAME_FLAGS.DS_NAME_NO_FLAGS, DS_NAME_FORMAT formatOffered = DS_NAME_FORMAT.DS_UNKNOWN_NAME, DS_NAME_FORMAT formatDesired = DS_NAME_FORMAT.DS_USER_PRINCIPAL_NAME) - { - IntPtr pResult; - uint err = Ntdsapi.DsCrackNames(handle, flags, formatOffered, formatDesired, (uint)(names?.Length ?? 0), names, out pResult); - if (err != (uint)DS_NAME_ERROR.DS_NAME_NO_ERROR) - throw new System.ComponentModel.Win32Exception((int)err); - try - { - // Next convert the returned structure to managed environment - DS_NAME_RESULT Result = (DS_NAME_RESULT)Marshal.PtrToStructure(pResult, typeof(DS_NAME_RESULT)); - return Result.Items; - } - finally - { + { + /// + /// Defines the errors returned by the status member of the DS_NAME_RESULT_ITEM structure. These are potential errors that may be encountered while a name is converted by the DsCrackNames function. + /// + public enum DS_NAME_ERROR : uint + { + /// The conversion was successful. + DS_NAME_NO_ERROR = 0, + + ///Generic processing error occurred. + DS_NAME_ERROR_RESOLVING = 1, + + ///The name cannot be found or the caller does not have permission to access the name. + DS_NAME_ERROR_NOT_FOUND = 2, + + ///The input name is mapped to more than one output name or the desired format did not have a single, unique value for the object found. + DS_NAME_ERROR_NOT_UNIQUE = 3, + + ///The input name was found, but the associated output format cannot be found. This can occur if the object does not have all the required attributes. + DS_NAME_ERROR_NO_MAPPING = 4, + + ///Unable to resolve entire name, but was able to determine in which domain object resides. The caller is expected to retry the call at a domain controller for the specified domain. The entire name cannot be resolved, but the domain that the object resides in could be determined. The pDomain member of the DS_NAME_RESULT_ITEM contains valid data when this error is specified. + DS_NAME_ERROR_DOMAIN_ONLY = 5, + + ///A syntactical mapping cannot be performed on the client without transmitting over the network. + DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 6, + + ///The name is from an external trusted forest. + DS_NAME_ERROR_TRUST_REFERRAL = 7 + } + + /// + /// Class that provides methods against a AD domain service. + /// + /// + [SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public class DomainService : IDisposable + { + IntPtr handle = IntPtr.Zero; + + /// + /// Initializes a new instance of the class. + /// + /// Name of the domain controller. + /// Name of the DNS domain. + /// + public DomainService(string domainControllerName = null, string dnsDomainName = null) + { + Ntdsapi.DsBind(domainControllerName, dnsDomainName, out handle); + } + + /// + /// Converts a directory service object name from any format to the UPN. + /// + /// The name to convert. + /// The corresponding UPN. + /// Unable to resolve user name. + public string CrackName(string name) + { + var res = CrackNames(new string[] { name }); + if (res == null || res.Length == 0 || res[0].status != DS_NAME_ERROR.DS_NAME_NO_ERROR) + throw new SecurityException("Unable to resolve user name."); + return res[0].pName; + } + + /// + /// Converts an array of directory service object names from one format to another. Name conversion enables client applications to map between the multiple names used to identify various directory service objects. + /// + /// The names to convert. + /// Values used to determine how the name syntax will be cracked. + /// Format of the input names. + /// Desired format for the output names. + /// An array of DS_NAME_RESULT_ITEM structures. Each element of this array represents a single converted name. + public DS_NAME_RESULT_ITEM[] CrackNames(string[] names = null, DS_NAME_FLAGS flags = DS_NAME_FLAGS.DS_NAME_NO_FLAGS, DS_NAME_FORMAT formatOffered = DS_NAME_FORMAT.DS_UNKNOWN_NAME, DS_NAME_FORMAT formatDesired = DS_NAME_FORMAT.DS_USER_PRINCIPAL_NAME) + { + IntPtr pResult; + uint err = Ntdsapi.DsCrackNames(handle, flags, formatOffered, formatDesired, (uint)(names?.Length ?? 0), names, out pResult); + if (err != (uint)DS_NAME_ERROR.DS_NAME_NO_ERROR) + throw new System.ComponentModel.Win32Exception((int)err); + try + { + // Next convert the returned structure to managed environment + DS_NAME_RESULT Result = (DS_NAME_RESULT)Marshal.PtrToStructure(pResult, typeof(DS_NAME_RESULT)); + return Result.Items; + } + finally + { Ntdsapi.DsFreeNameResult(pResult); - } - } - - public void Dispose() - { - uint ret = Ntdsapi.DsUnBind(ref handle); - System.Diagnostics.Debug.WriteLineIf(ret != 0, "Error unbinding :\t" + ret.ToString()); - } - } - - - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public struct DS_NAME_RESULT - { - public uint cItems; - internal IntPtr rItems; // PDS_NAME_RESULT_ITEM - - public DS_NAME_RESULT_ITEM[] Items - { - get - { - if (rItems == IntPtr.Zero) - return new DS_NAME_RESULT_ITEM[0]; - var ResultArray = new DS_NAME_RESULT_ITEM[cItems]; - Type strType = typeof(DS_NAME_RESULT_ITEM); - int stSize = Marshal.SizeOf(strType); - IntPtr curptr; - for (uint i = 0; i < cItems; i++) - { - curptr = new IntPtr(rItems.ToInt64() + (i * stSize)); - ResultArray[i] = (DS_NAME_RESULT_ITEM)Marshal.PtrToStructure(curptr, strType); - } - return ResultArray; - } - } - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public struct DS_NAME_RESULT_ITEM - { - public DS_NAME_ERROR status; - public string pDomain; - public string pName; - - public override string ToString() - { - if (status == DS_NAME_ERROR.DS_NAME_NO_ERROR) - return pName; - return string.Empty; - } - } - } + } + } + + public void Dispose() + { + uint ret = Ntdsapi.DsUnBind(ref handle); + System.Diagnostics.Debug.WriteLineIf(ret != 0, "Error unbinding :\t" + ret.ToString()); + } + } + + + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct DS_NAME_RESULT + { + public uint cItems; + internal IntPtr rItems; // PDS_NAME_RESULT_ITEM + + public DS_NAME_RESULT_ITEM[] Items + { + get + { + if (rItems == IntPtr.Zero) + return new DS_NAME_RESULT_ITEM[0]; + var ResultArray = new DS_NAME_RESULT_ITEM[cItems]; + Type strType = typeof(DS_NAME_RESULT_ITEM); + int stSize = Marshal.SizeOf(strType); + IntPtr curptr; + for (uint i = 0; i < cItems; i++) + { + curptr = new IntPtr(rItems.ToInt64() + (i * stSize)); + ResultArray[i] = (DS_NAME_RESULT_ITEM)Marshal.PtrToStructure(curptr, strType); + } + return ResultArray; + } + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct DS_NAME_RESULT_ITEM + { + public DS_NAME_ERROR status; + public string pDomain; + public string pName; + + public override string ToString() + { + if (status == DS_NAME_ERROR.DS_NAME_NO_ERROR) + return pName; + return string.Empty; + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NetServerEnum.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NetServerEnum.cs index aeaa3972b..5013b432c 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NetServerEnum.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/NetServerEnum.cs @@ -7,131 +7,131 @@ namespace winPEAS.TaskScheduler.TaskEditor.Native { internal static partial class NativeMethods - { - const int MAX_PREFERRED_LENGTH = -1; + { + const int MAX_PREFERRED_LENGTH = -1; - public enum ServerPlatform - { - DOS = 300, - OS2 = 400, - NT = 500, - OSF = 600, - VMS = 700 - } + public enum ServerPlatform + { + DOS = 300, + OS2 = 400, + NT = 500, + OSF = 600, + VMS = 700 + } - [StructLayout(LayoutKind.Sequential)] - public struct SERVER_INFO_100 - { - public ServerPlatform PlatformId; - [MarshalAs(UnmanagedType.LPWStr)] - public string Name; - } + [StructLayout(LayoutKind.Sequential)] + public struct SERVER_INFO_100 + { + public ServerPlatform PlatformId; + [MarshalAs(UnmanagedType.LPWStr)] + public string Name; + } - [StructLayout(LayoutKind.Sequential)] - public struct SERVER_INFO_101 - { - public ServerPlatform PlatformId; - [MarshalAs(UnmanagedType.LPWStr)] - public string Name; - public int VersionMajor; - public int VersionMinor; - public ServerTypes Type; - [MarshalAs(UnmanagedType.LPWStr)] - public string Comment; - } + [StructLayout(LayoutKind.Sequential)] + public struct SERVER_INFO_101 + { + public ServerPlatform PlatformId; + [MarshalAs(UnmanagedType.LPWStr)] + public string Name; + public int VersionMajor; + public int VersionMinor; + public ServerTypes Type; + [MarshalAs(UnmanagedType.LPWStr)] + public string Comment; + } - [StructLayout(LayoutKind.Sequential)] - public struct SERVER_INFO_102 - { - public ServerPlatform PlatformId; - [MarshalAs(UnmanagedType.LPWStr)] - public string Name; - public int VersionMajor; - public int VersionMinor; - public ServerTypes Type; - [MarshalAs(UnmanagedType.LPWStr)] - public string Comment; - public int MaxUsers; - public int AutoDisconnectMinutes; - [MarshalAs(UnmanagedType.Bool)] - public bool Hidden; - public int NetworkAnnounceRate; - public int NetworkAnnounceRateDelta; - public int UsersPerLicense; - [MarshalAs(UnmanagedType.LPWStr)] - public string UserDirectoryPath; - } + [StructLayout(LayoutKind.Sequential)] + public struct SERVER_INFO_102 + { + public ServerPlatform PlatformId; + [MarshalAs(UnmanagedType.LPWStr)] + public string Name; + public int VersionMajor; + public int VersionMinor; + public ServerTypes Type; + [MarshalAs(UnmanagedType.LPWStr)] + public string Comment; + public int MaxUsers; + public int AutoDisconnectMinutes; + [MarshalAs(UnmanagedType.Bool)] + public bool Hidden; + public int NetworkAnnounceRate; + public int NetworkAnnounceRateDelta; + public int UsersPerLicense; + [MarshalAs(UnmanagedType.LPWStr)] + public string UserDirectoryPath; + } - [StructLayout(LayoutKind.Sequential)] - public struct NetworkComputerInfo // SERVER_INFO_101 - { - ServerPlatform sv101_platform_id; - [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] - string sv101_name; - int sv101_version_major; - int sv101_version_minor; - ServerTypes sv101_type; - [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] - string sv101_comment; + [StructLayout(LayoutKind.Sequential)] + public struct NetworkComputerInfo // SERVER_INFO_101 + { + ServerPlatform sv101_platform_id; + [MarshalAs(UnmanagedType.LPWStr)] + string sv101_name; + int sv101_version_major; + int sv101_version_minor; + ServerTypes sv101_type; + [MarshalAs(UnmanagedType.LPWStr)] + string sv101_comment; - public ServerPlatform Platform => sv101_platform_id; - public string Name => sv101_name; - public string Comment => sv101_comment; - public ServerTypes ServerTypes => sv101_type; - public Version Version => new Version(sv101_version_major, sv101_version_minor); - }; + public ServerPlatform Platform => sv101_platform_id; + public string Name => sv101_name; + public string Comment => sv101_comment; + public ServerTypes ServerTypes => sv101_type; + public Version Version => new Version(sv101_version_major, sv101_version_minor); + }; - public static IEnumerable GetNetworkComputerNames(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null) => - Array.ConvertAll(NetServerEnum(serverTypes, domain), si => si.Name); + public static IEnumerable GetNetworkComputerNames(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null) => + Array.ConvertAll(NetServerEnum(serverTypes, domain), si => si.Name); - public static IEnumerable GetNetworkComputerInfo(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null) => - NetServerEnum(serverTypes, domain, 101); + public static IEnumerable GetNetworkComputerInfo(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null) => + NetServerEnum(serverTypes, domain, 101); - public static T[] NetServerEnum(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null, int level = 0) where T : struct - { - if (level == 0) - level = int.Parse(System.Text.RegularExpressions.Regex.Replace(typeof(T).Name, @"[^\d]", "")); + public static T[] NetServerEnum(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null, int level = 0) where T : struct + { + if (level == 0) + level = int.Parse(System.Text.RegularExpressions.Regex.Replace(typeof(T).Name, @"[^\d]", "")); - IntPtr bufptr = IntPtr.Zero; - try - { - int entriesRead, totalEntries; - IntPtr resumeHandle = IntPtr.Zero; + IntPtr bufptr = IntPtr.Zero; + try + { + int entriesRead, totalEntries; + IntPtr resumeHandle = IntPtr.Zero; - int ret = Netapi32.NetServerEnum(null, level, out bufptr, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, serverTypes, domain, resumeHandle); - if (ret == 0) - return InteropUtil.ToArray(bufptr, entriesRead); - throw new System.ComponentModel.Win32Exception(ret); - } - finally - { + int ret = Netapi32.NetServerEnum(null, level, out bufptr, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, serverTypes, domain, resumeHandle); + if (ret == 0) + return InteropUtil.ToArray(bufptr, entriesRead); + throw new System.ComponentModel.Win32Exception(ret); + } + finally + { Netapi32.NetApiBufferFree(bufptr); - } - } + } + } - public static T NetServerGetInfo(string serverName, int level = 0) where T : struct - { - if (level == 0) - level = int.Parse(System.Text.RegularExpressions.Regex.Replace(typeof(T).Name, @"[^\d]", "")); + public static T NetServerGetInfo(string serverName, int level = 0) where T : struct + { + if (level == 0) + level = int.Parse(System.Text.RegularExpressions.Regex.Replace(typeof(T).Name, @"[^\d]", "")); - IntPtr ptr = IntPtr.Zero; - try - { - int ret = Netapi32.NetServerGetInfo(serverName, level, out ptr); + IntPtr ptr = IntPtr.Zero; + try + { + int ret = Netapi32.NetServerGetInfo(serverName, level, out ptr); if (ret != 0) { throw new System.ComponentModel.Win32Exception(ret); } - return (T)Marshal.PtrToStructure(ptr, typeof(T)); - } - finally - { + return (T)Marshal.PtrToStructure(ptr, typeof(T)); + } + finally + { if (ptr != IntPtr.Zero) { Netapi32.NetApiBufferFree(ptr); } - } - } - } + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/SYSTEMTIME.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/SYSTEMTIME.cs index 501cef406..9705db5ad 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/SYSTEMTIME.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEditor/Native/SYSTEMTIME.cs @@ -3,114 +3,114 @@ namespace winPEAS.TaskScheduler.TaskEditor.Native { - internal static partial class NativeMethods - { - [StructLayout(LayoutKind.Sequential, Pack = 2)] - internal struct SYSTEMTIME : IConvertible - { - public ushort Year; - public ushort Month; - public ushort DayOfWeek; - public ushort Day; - public ushort Hour; - public ushort Minute; - public ushort Second; - public ushort Milliseconds; + internal static partial class NativeMethods + { + [StructLayout(LayoutKind.Sequential, Pack = 2)] + internal struct SYSTEMTIME : IConvertible + { + public ushort Year; + public ushort Month; + public ushort DayOfWeek; + public ushort Day; + public ushort Hour; + public ushort Minute; + public ushort Second; + public ushort Milliseconds; - public SYSTEMTIME(DateTime dt) - { - dt = dt.ToLocalTime(); - Year = Convert.ToUInt16(dt.Year); - Month = Convert.ToUInt16(dt.Month); - DayOfWeek = Convert.ToUInt16(dt.DayOfWeek); - Day = Convert.ToUInt16(dt.Day); - Hour = Convert.ToUInt16(dt.Hour); - Minute = Convert.ToUInt16(dt.Minute); - Second = Convert.ToUInt16(dt.Second); - Milliseconds = Convert.ToUInt16(dt.Millisecond); - } + public SYSTEMTIME(DateTime dt) + { + dt = dt.ToLocalTime(); + Year = Convert.ToUInt16(dt.Year); + Month = Convert.ToUInt16(dt.Month); + DayOfWeek = Convert.ToUInt16(dt.DayOfWeek); + Day = Convert.ToUInt16(dt.Day); + Hour = Convert.ToUInt16(dt.Hour); + Minute = Convert.ToUInt16(dt.Minute); + Second = Convert.ToUInt16(dt.Second); + Milliseconds = Convert.ToUInt16(dt.Millisecond); + } - public SYSTEMTIME(ushort year, ushort month, ushort day, ushort hour = 0, ushort minute = 0, ushort second = 0, ushort millisecond = 0) - { - Year = year; - Month = month; - Day = day; - Hour = hour; - Minute = minute; - Second = second; - Milliseconds = millisecond; - DayOfWeek = 0; - } + public SYSTEMTIME(ushort year, ushort month, ushort day, ushort hour = 0, ushort minute = 0, ushort second = 0, ushort millisecond = 0) + { + Year = year; + Month = month; + Day = day; + Hour = hour; + Minute = minute; + Second = second; + Milliseconds = millisecond; + DayOfWeek = 0; + } - public static implicit operator DateTime(SYSTEMTIME st) - { - if (st.Year == 0 || st == MinValue) - return DateTime.MinValue; - if (st == MaxValue) - return DateTime.MaxValue; - return new DateTime(st.Year, st.Month, st.Day, st.Hour, st.Minute, st.Second, st.Milliseconds, DateTimeKind.Local); - } + public static implicit operator DateTime(SYSTEMTIME st) + { + if (st.Year == 0 || st == MinValue) + return DateTime.MinValue; + if (st == MaxValue) + return DateTime.MaxValue; + return new DateTime(st.Year, st.Month, st.Day, st.Hour, st.Minute, st.Second, st.Milliseconds, DateTimeKind.Local); + } - public static implicit operator SYSTEMTIME(DateTime dt) => new SYSTEMTIME(dt); + public static implicit operator SYSTEMTIME(DateTime dt) => new SYSTEMTIME(dt); - public static bool operator ==(SYSTEMTIME s1, SYSTEMTIME s2) => (s1.Year == s2.Year && s1.Month == s2.Month && s1.Day == s2.Day && s1.Hour == s2.Hour && s1.Minute == s2.Minute && s1.Second == s2.Second && s1.Milliseconds == s2.Milliseconds); + public static bool operator ==(SYSTEMTIME s1, SYSTEMTIME s2) => (s1.Year == s2.Year && s1.Month == s2.Month && s1.Day == s2.Day && s1.Hour == s2.Hour && s1.Minute == s2.Minute && s1.Second == s2.Second && s1.Milliseconds == s2.Milliseconds); - public static bool operator !=(SYSTEMTIME s1, SYSTEMTIME s2) => !(s1 == s2); + public static bool operator !=(SYSTEMTIME s1, SYSTEMTIME s2) => !(s1 == s2); - public static readonly SYSTEMTIME MinValue, MaxValue; + public static readonly SYSTEMTIME MinValue, MaxValue; - static SYSTEMTIME() - { - MinValue = new SYSTEMTIME(1601, 1, 1); - MaxValue = new SYSTEMTIME(30827, 12, 31, 23, 59, 59, 999); - } + static SYSTEMTIME() + { + MinValue = new SYSTEMTIME(1601, 1, 1); + MaxValue = new SYSTEMTIME(30827, 12, 31, 23, 59, 59, 999); + } - public override bool Equals(object obj) - { - if (obj is SYSTEMTIME) - return ((SYSTEMTIME)obj) == this; - if (obj is DateTime) - return ((DateTime)this).Equals(obj); - return base.Equals(obj); - } + public override bool Equals(object obj) + { + if (obj is SYSTEMTIME) + return ((SYSTEMTIME)obj) == this; + if (obj is DateTime) + return ((DateTime)this).Equals(obj); + return base.Equals(obj); + } - public override int GetHashCode() => ((DateTime)this).GetHashCode(); + public override int GetHashCode() => ((DateTime)this).GetHashCode(); - public override string ToString() => ((DateTime)this).ToString(); + public override string ToString() => ((DateTime)this).ToString(); - TypeCode IConvertible.GetTypeCode() => ((IConvertible)(DateTime)this).GetTypeCode(); + TypeCode IConvertible.GetTypeCode() => ((IConvertible)(DateTime)this).GetTypeCode(); - bool IConvertible.ToBoolean(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToBoolean(provider); + bool IConvertible.ToBoolean(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToBoolean(provider); - byte IConvertible.ToByte(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToByte(provider); + byte IConvertible.ToByte(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToByte(provider); - char IConvertible.ToChar(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToChar(provider); + char IConvertible.ToChar(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToChar(provider); - DateTime IConvertible.ToDateTime(IFormatProvider provider) => (DateTime)this; + DateTime IConvertible.ToDateTime(IFormatProvider provider) => (DateTime)this; - decimal IConvertible.ToDecimal(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToDecimal(provider); + decimal IConvertible.ToDecimal(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToDecimal(provider); - double IConvertible.ToDouble(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToDouble(provider); + double IConvertible.ToDouble(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToDouble(provider); - short IConvertible.ToInt16(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt16(provider); + short IConvertible.ToInt16(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt16(provider); - int IConvertible.ToInt32(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt32(provider); + int IConvertible.ToInt32(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt32(provider); - long IConvertible.ToInt64(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt64(provider); + long IConvertible.ToInt64(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt64(provider); - sbyte IConvertible.ToSByte(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToSByte(provider); + sbyte IConvertible.ToSByte(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToSByte(provider); - float IConvertible.ToSingle(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToSingle(provider); + float IConvertible.ToSingle(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToSingle(provider); - string IConvertible.ToString(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToString(provider); + string IConvertible.ToString(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToString(provider); - object IConvertible.ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)(DateTime)this).ToType(conversionType, provider); + object IConvertible.ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)(DateTime)this).ToType(conversionType, provider); - ushort IConvertible.ToUInt16(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt16(provider); + ushort IConvertible.ToUInt16(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt16(provider); - uint IConvertible.ToUInt32(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt32(provider); + uint IConvertible.ToUInt32(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt32(provider); - ulong IConvertible.ToUInt64(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt64(provider); - } - } + ulong IConvertible.ToUInt64(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt64(provider); + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEvent.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEvent.cs index 499c71a3b..4485da118 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEvent.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskEvent.cs @@ -1,878 +1,875 @@ using System; using System.Collections.Generic; using System.Diagnostics.Eventing.Reader; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - /// - /// Changes to tasks and the engine that cause events. - /// - public enum StandardTaskEventId - { - /// Task Scheduler started an instance of a task for a user. - /// For detailed information, see the documentation for Event ID 100 on TechNet. - JobStart = 100, - /// Task Scheduler failed to start a task for a user. - /// For detailed information, see the documentation for Event ID 101 on TechNet. - JobStartFailed = 101, - /// Task Scheduler successfully finished an instance of a task for a user. - /// For detailed information, see the documentation for Event ID 102 on TechNet. - JobSuccess = 102, - /// Task Scheduler failed to start an instance of a task for a user. - /// For detailed information, see the documentation for Event ID 103 on TechNet. - JobFailure = 103, - /// Task Scheduler failed to log on the user. - /// For detailed information, see the documentation for Event ID 104 on TechNet. - LogonFailure = 104, - /// Task Scheduler failed to impersonate a user. - /// For detailed information, see the documentation for Event ID 105 on TechNet. - ImpersonationFailure = 105, - /// The a user registered the Task Scheduler a task. - /// For detailed information, see the documentation for Event ID 106 on TechNet. - JobRegistered = 106, - /// Task Scheduler launched an instance of a task due to a time trigger. - /// For detailed information, see the documentation for Event ID 107 on TechNet. - TimeTrigger = 107, - /// Task Scheduler launched an instance of a task due to an event trigger. - /// For detailed information, see the documentation for Event ID 108 on TechNet. - EventTrigger = 108, - /// Task Scheduler launched an instance of a task due to a registration trigger. - /// For detailed information, see the documentation for Event ID 109 on TechNet. - ImmediateTrigger = 109, - /// Task Scheduler launched an instance of a task for a user. - /// For detailed information, see the documentation for Event ID 110 on TechNet. - Run = 110, - /// Task Scheduler terminated an instance of a task due to exceeding the time allocated for execution, as configured in the task definition. - /// For detailed information, see the documentation for Event ID 111 on TechNet. - JobTermination = 111, - /// Task Scheduler could not start a task because the network was unavailable. Ensure the computer is connected to the required network as specified in the task. - /// For detailed information, see the documentation for Event ID 112 on TechNet. - JobNoStartWithoutNetwork = 112, - /// The Task Scheduler registered the a task, but not all the specified triggers will start the task. Ensure all the task triggers are valid. - /// For detailed information, see the documentation for Event ID 113 on TechNet. - TaskRegisteredWithoutSomeTriggers = 113, - /// Task Scheduler could not launch a task as scheduled. Instance is started now as required by the configuration option to start the task when available, if the scheduled time is missed. - /// For detailed information, see the documentation for Event ID 114 on TechNet. - MissedTaskLaunched = 114, - /// Task Scheduler failed to roll back a transaction when updating or deleting a task. - /// For detailed information, see the documentation for Event ID 115 on TechNet. - TransactionRollbackFailure = 115, - /// Task Scheduler saved the configuration for a task, but the credentials used to run the task could not be stored. - /// For detailed information, see the documentation for Event ID 116 on TechNet. - TaskRegisteredWithoutCredentials = 116, - /// Task Scheduler launched an instance of a task due to an idle condition. - /// For detailed information, see the documentation for Event ID 117 on TechNet. - IdleTrigger = 117, - /// Task Scheduler launched an instance of a task due to system startup. - /// For detailed information, see the documentation for Event ID 118 on TechNet. - BootTrigger = 118, - /// Task Scheduler launched an instance of a task due to a user logon. - /// For detailed information, see the documentation for Event ID 119 on TechNet. - LogonTrigger = 119, - /// Task Scheduler launched an instance of a task due to a user connecting to the console. - /// For detailed information, see the documentation for Event ID 120 on TechNet. - ConsoleConnectTrigger = 120, - /// Task Scheduler launched an instance of a task due to a user disconnecting from the console. - /// For detailed information, see the documentation for Event ID 121 on TechNet. - ConsoleDisconnectTrigger = 121, - /// Task Scheduler launched an instance of a task due to a user remotely connecting. - /// For detailed information, see the documentation for Event ID 122 on TechNet. - RemoteConnectTrigger = 122, - /// Task Scheduler launched an instance of a task due to a user remotely disconnecting. - /// For detailed information, see the documentation for Event ID 123 on TechNet. - RemoteDisconnectTrigger = 123, - /// Task Scheduler launched an instance of a task due to a user locking the computer. - /// For detailed information, see the documentation for Event ID 124 on TechNet. - SessionLockTrigger = 124, - /// Task Scheduler launched an instance of a task due to a user unlocking the computer. - /// For detailed information, see the documentation for Event ID 125 on TechNet. - SessionUnlockTrigger = 125, - /// Task Scheduler failed to execute a task. Task Scheduler is attempting to restart the task. - /// For detailed information, see the documentation for Event ID 126 on TechNet. - FailedTaskRestart = 126, - /// Task Scheduler failed to execute a task due to a shutdown race condition. Task Scheduler is attempting to restart the task. - /// For detailed information, see the documentation for Event ID 127 on TechNet. - RejectedTaskRestart = 127, - /// Task Scheduler did not launch a task because the current time exceeds the configured task end time. - /// For detailed information, see the documentation for Event ID 128 on TechNet. - IgnoredTaskStart = 128, - /// Task Scheduler launched an instance of a task in a new process. - /// For detailed information, see the documentation for Event ID 129 on TechNet. - CreatedTaskProcess = 129, - /// The Task Scheduler service failed to start a task due to the service being busy. - /// For detailed information, see the documentation for Event ID 130 on TechNet. - TaskNotRunServiceBusy = 130, - /// Task Scheduler failed to start a task because the number of tasks in the task queue exceeds the quota currently configured. - /// For detailed information, see the documentation for Event ID 131 on TechNet. - TaskNotStartedTaskQueueQuotaExceeded = 131, - /// The Task Scheduler task launching queue quota is approaching its preset limit of tasks currently configured. - /// For detailed information, see the documentation for Event ID 132 on TechNet. - TaskQueueQuotaApproaching = 132, - /// Task Scheduler failed to start a task in the task engine for a user. - /// For detailed information, see the documentation for Event ID 133 on TechNet. - TaskNotStartedEngineQuotaExceeded = 133, - /// Task Engine for a user is approaching its preset limit of tasks. - /// For detailed information, see the documentation for Event ID 134 on TechNet. - EngineQuotaApproaching = 134, - /// Task Scheduler did not launch a task because launch condition not met, machine not idle. - /// For detailed information, see the documentation for Event ID 135 on TechNet. - NotStartedWithoutIdle = 135, - /// A user updated Task Scheduler a task - /// For detailed information, see the documentation for Event ID 140 on TechNet. - TaskUpdated = 140, - /// A user deleted Task Scheduler a task - /// For detailed information, see the documentation for Event ID 141 on TechNet. - TaskDeleted = 141, - /// A user disabled Task Scheduler a task - /// For detailed information, see the documentation for Event ID 142 on TechNet. - TaskDisabled = 142, - /// Task Scheduler woke up the computer to run a task. - /// For detailed information, see the documentation for Event ID 145 on TechNet. - TaskStartedOnComputerWakeup = 145, - /// Task Scheduler failed to subscribe the event trigger for a task. - /// For detailed information, see the documentation for Event ID 150 on TechNet. - TaskEventSubscriptionFailed = 150, - /// Task Scheduler launched an action in an instance of a task. - /// For detailed information, see the documentation for Event ID 200 on TechNet. - ActionStart = 200, - /// Task Scheduler successfully completed a task instance and action. - /// For detailed information, see the documentation for Event ID 201 on TechNet. - ActionSuccess = 201, - /// Task Scheduler failed to complete an instance of a task with an action. - /// For detailed information, see the documentation for Event ID 202 on TechNet. - ActionFailure = 202, - /// Task Scheduler failed to launch an action in a task instance. - /// For detailed information, see the documentation for Event ID 203 on TechNet. - ActionLaunchFailure = 203, - /// Task Scheduler failed to retrieve the event triggering values for a task . The event will be ignored. - /// For detailed information, see the documentation for Event ID 204 on TechNet. - EventRenderFailed = 204, - /// Task Scheduler failed to match the pattern of events for a task. The events will be ignored. - /// For detailed information, see the documentation for Event ID 205 on TechNet. - EventAggregateFailed = 205, - /// Task Scheduler is shutting down the a task engine. - /// For detailed information, see the documentation for Event ID 301 on TechNet. - SessionExit = 301, - /// Task Scheduler is shutting down the a task engine due to an error. - /// For detailed information, see the documentation for Event ID 303 on TechNet. - SessionError = 303, - /// Task Scheduler sent a task to a task engine. - /// For detailed information, see the documentation for Event ID 304 on TechNet. - SessionSentJob = 304, - /// Task Scheduler did not send a task to a task engine. - /// For detailed information, see the documentation for Event ID 305 on TechNet. - SessionSentJobFailed = 305, - /// For a Task Scheduler task engine, the thread pool failed to process the message. - /// For detailed information, see the documentation for Event ID 306 on TechNet. - SessionFailedToProcessMessage = 306, - /// The Task Scheduler service failed to connect to a task engine process. - /// For detailed information, see the documentation for Event ID 307 on TechNet. - SessionManagerConnectFailed = 307, - /// Task Scheduler connected to a task engine process. - /// For detailed information, see the documentation for Event ID 308 on TechNet. - SessionConnected = 308, - /// There are Task Scheduler tasks orphaned during a task engine shutdown. - /// For detailed information, see the documentation for Event ID 309 on TechNet. - SessionJobsOrphaned = 309, - /// Task Scheduler started a task engine process. - /// For detailed information, see the documentation for Event ID 310 on TechNet. - SessionProcessStarted = 310, - /// Task Scheduler failed to start a task engine process due to an error. - /// For detailed information, see the documentation for Event ID 311 on TechNet. - SessionProcessLaunchFailed = 311, - /// Task Scheduler created the Win32 job object for a task engine. - /// For detailed information, see the documentation for Event ID 312 on TechNet. - SessionWin32ObjectCreated = 312, - /// The Task Scheduler channel is ready to send and receive messages. - /// For detailed information, see the documentation for Event ID 313 on TechNet. - SessionChannelReady = 313, - /// Task Scheduler has no tasks running for a task engine, and the idle timer has started. - /// For detailed information, see the documentation for Event ID 314 on TechNet. - SessionIdle = 314, - /// A task engine process failed to connect to the Task Scheduler service. - /// For detailed information, see the documentation for Event ID 315 on TechNet. - SessionProcessConnectFailed = 315, - /// A task engine failed to send a message to the Task Scheduler service. - /// For detailed information, see the documentation for Event ID 316 on TechNet. - SessionMessageSendFailed = 316, - /// Task Scheduler started a task engine process. - /// For detailed information, see the documentation for Event ID 317 on TechNet. - SessionProcessMainStarted = 317, - /// Task Scheduler shut down a task engine process. - /// For detailed information, see the documentation for Event ID 318 on TechNet. - SessionProcessMainShutdown = 318, - /// A task engine received a message from the Task Scheduler service requesting to launch a task. - /// For detailed information, see the documentation for Event ID 319 on TechNet. - SessionProcessReceivedStartJob = 319, - /// A task engine received a message from the Task Scheduler service requesting to stop a task instance. - /// For detailed information, see the documentation for Event ID 320 on TechNet. - SessionProcessReceivedStopJob = 320, - /// Task Scheduler did not launch a task because an instance of the same task is already running. - /// For detailed information, see the documentation for Event ID 322 on TechNet. - NewInstanceIgnored = 322, - /// Task Scheduler stopped an instance of a task in order to launch a new instance. - /// For detailed information, see the documentation for Event ID 323 on TechNet. - RunningInstanceStopped = 323, - /// Task Scheduler queued an instance of a task and will launch it as soon as another instance completes. - /// For detailed information, see the documentation for Event ID 324 on TechNet. - NewInstanceQueued = 324, - /// Task Scheduler queued an instance of a task that will launch immediately. - /// For detailed information, see the documentation for Event ID 325 on TechNet. - InstanceQueued = 325, - /// Task Scheduler did not launch a task because the computer is running on batteries. If launching the task on batteries is required, change the respective flag in the task configuration. - /// For detailed information, see the documentation for Event ID 326 on TechNet. - NoStartOnBatteries = 326, - /// Task Scheduler stopped an instance of a task because the computer is switching to battery power. - /// For detailed information, see the documentation for Event ID 327 on TechNet. - StoppingOnBatteries = 327, - /// Task Scheduler stopped an instance of a task because the computer is no longer idle. - /// For detailed information, see the documentation for Event ID 328 on TechNet. - StoppingOffIdle = 328, - /// Task Scheduler stopped an instance of a task because the task timed out. - /// For detailed information, see the documentation for Event ID 329 on TechNet. - StoppingOnTimeout = 329, - /// Task Scheduler stopped an instance of a task as request by a user . - /// For detailed information, see the documentation for Event ID 330 on TechNet. - StoppingOnRequest = 330, - /// Task Scheduler will continue to execute an instance of a task even after the designated timeout, due to a failure to create the timeout mechanism. - /// For detailed information, see the documentation for Event ID 331 on TechNet. - TimeoutWontWork = 331, - /// Task Scheduler did not launch a task because a user was not logged on when the launching conditions were met. Ensure the user is logged on or change the task definition to allow the task to launch when the user is logged off. - /// For detailed information, see the documentation for Event ID 332 on TechNet. - NoStartUserNotLoggedOn = 332, - /// The Task Scheduler service has started. - /// For detailed information, see the documentation for Event ID 400 on TechNet. - ScheduleServiceStart = 400, - /// The Task Scheduler service failed to start due to an error. - /// For detailed information, see the documentation for Event ID 401 on TechNet. - ScheduleServiceStartFailed = 401, - /// Task Scheduler service is shutting down. - /// For detailed information, see the documentation for Event ID 402 on TechNet. - ScheduleServiceStop = 402, - /// The Task Scheduler service has encountered an error. - /// For detailed information, see the documentation for Event ID 403 on TechNet. - ScheduleServiceError = 403, - /// The Task Scheduler service has encountered an RPC initialization error. - /// For detailed information, see the documentation for Event ID 404 on TechNet. - ScheduleServiceRpcInitError = 404, - /// The Task Scheduler service has failed to initialize COM. - /// For detailed information, see the documentation for Event ID 405 on TechNet. - ScheduleServiceComInitError = 405, - /// The Task Scheduler service failed to initialize the credentials store. - /// For detailed information, see the documentation for Event ID 406 on TechNet. - ScheduleServiceCredStoreInitError = 406, - /// Task Scheduler service failed to initialize LSA. - /// For detailed information, see the documentation for Event ID 407 on TechNet. - ScheduleServiceLsaInitError = 407, - /// Task Scheduler service failed to initialize idle state detection module. Idle tasks may not be started as required. - /// For detailed information, see the documentation for Event ID 408 on TechNet. - ScheduleServiceIdleServiceInitError = 408, - /// The Task Scheduler service failed to initialize a time change notification. System time updates may not be picked by the service and task schedules may not be updated. - /// For detailed information, see the documentation for Event ID 409 on TechNet. - ScheduleServiceTimeChangeInitError = 409, - /// Task Scheduler service received a time system change notification. - /// For detailed information, see the documentation for Event ID 411 on TechNet. - ScheduleServiceTimeChangeSignaled = 411, - /// Task Scheduler service failed to launch tasks triggered by computer startup. Restart the Task Scheduler service. - /// For detailed information, see the documentation for Event ID 412 on TechNet. - ScheduleServiceRunBootJobsFailed = 412, - /// Task Scheduler service started Task Compatibility module. - /// For detailed information, see the documentation for Event ID 700 on TechNet. - CompatStart = 700, - /// Task Scheduler service failed to start Task Compatibility module. Tasks may not be able to register on previous Window versions. - /// For detailed information, see the documentation for Event ID 701 on TechNet. - CompatStartFailed = 701, - /// Task Scheduler failed to initialize the RPC server for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions. - /// For detailed information, see the documentation for Event ID 702 on TechNet. - CompatStartRpcFailed = 702, - /// Task Scheduler failed to initialize Net Schedule API for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions. - /// For detailed information, see the documentation for Event ID 703 on TechNet. - CompatStartNetscheduleFailed = 703, - /// Task Scheduler failed to initialize LSA for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions. - /// For detailed information, see the documentation for Event ID 704 on TechNet. - CompatStartLsaFailed = 704, - /// Task Scheduler failed to start directory monitoring for the Task Compatibility module. - /// For detailed information, see the documentation for Event ID 705 on TechNet. - CompatDirectoryMonitorFailed = 705, - /// Task Compatibility module failed to update a task to the required status. - /// For detailed information, see the documentation for Event ID 706 on TechNet. - CompatTaskStatusUpdateFailed = 706, - /// Task Compatibility module failed to delete a task. - /// For detailed information, see the documentation for Event ID 707 on TechNet. - CompatTaskDeleteFailed = 707, - /// Task Compatibility module failed to set a security descriptor for a task. - /// For detailed information, see the documentation for Event ID 708 on TechNet. - CompatTaskSetSdFailed = 708, - /// Task Compatibility module failed to update a task. - /// For detailed information, see the documentation for Event ID 709 on TechNet. - CompatTaskUpdateFailed = 709, - /// Task Compatibility module failed to upgrade existing tasks. Upgrade will be attempted again next time 'Task Scheduler' service starts. - /// For detailed information, see the documentation for Event ID 710 on TechNet. - CompatUpgradeStartFailed = 710, - /// Task Compatibility module failed to upgrade NetSchedule account. - /// For detailed information, see the documentation for Event ID 711 on TechNet. - CompatUpgradeNsAccountFailed = 711, - /// Task Compatibility module failed to read existing store to upgrade tasks. - /// For detailed information, see the documentation for Event ID 712 on TechNet. - CompatUpgradeStoreEnumFailed = 712, - /// Task Compatibility module failed to load a task for upgrade. - /// For detailed information, see the documentation for Event ID 713 on TechNet. - CompatUpgradeTaskLoadFailed = 713, - /// Task Compatibility module failed to register a task for upgrade. - /// For detailed information, see the documentation for Event ID 714 on TechNet. - CompatUpgradeTaskRegistrationFailed = 714, - /// Task Compatibility module failed to delete LSA store for upgrade. - /// For detailed information, see the documentation for Event ID 715 on TechNet. - CompatUpgradeLsaCleanupFailed = 715, - /// Task Compatibility module failed to upgrade existing scheduled tasks. - /// For detailed information, see the documentation for Event ID 716 on TechNet. - CompatUpgradeFailed = 716, - /// Task Compatibility module failed to determine if upgrade is needed. - /// For detailed information, see the documentation for Event ID 717 on TechNet. - CompatUpgradeNeedNotDetermined = 717, - /// Task scheduler was unable to upgrade the credential store from the Beta 2 version. You may need to re-register any tasks that require passwords. - /// For detailed information, see the documentation for Event ID 718 on TechNet. - VistaBeta2CredstoreUpgradeFailed = 718, - /// A unknown value. - Unknown = -2 - } - - /// - /// Historical event information for a task. This class wraps and extends the class. - /// - /// - /// For events on systems prior to Windows Vista, this class will only have information for the TaskPath, TimeCreated and EventId properties. - /// - [PublicAPI] - public sealed class TaskEvent : IComparable - { - internal TaskEvent([NotNull] EventRecord rec) - { - EventId = rec.Id; - EventRecord = rec; - Version = rec.Version; - TaskCategory = rec.TaskDisplayName; - OpCode = rec.OpcodeDisplayName; - TimeCreated = rec.TimeCreated; - RecordId = rec.RecordId; - ActivityId = rec.ActivityId; - Level = rec.LevelDisplayName; - UserId = rec.UserId; - ProcessId = rec.ProcessId; - TaskPath = rec.Properties.Count > 0 ? rec.Properties[0]?.Value?.ToString() : null; - DataValues = new EventDataValues(rec as EventLogRecord); - } - - internal TaskEvent([NotNull] string taskPath, StandardTaskEventId id, DateTime time) - { - EventId = (int)id; - TaskPath = taskPath; - TimeCreated = time; - } - - /// - /// Gets the activity id. This value is null for V1 events. - /// - public Guid? ActivityId { get; internal set; } - - /// - /// An indexer that gets the value of each of the data item values. This value is null for V1 events. - /// - /// - /// The data values. - /// - public EventDataValues DataValues { get; } - - /// - /// Gets the event id. - /// - public int EventId { get; internal set; } - - /// - /// Gets the underlying . This value is null for V1 events. - /// - public EventRecord EventRecord { get; internal set; } - - /// - /// Gets the from the . - /// - /// - /// The . If not found, returns . - /// - public StandardTaskEventId StandardEventId - { - get - { - if (Enum.IsDefined(typeof(StandardTaskEventId), EventId)) - return (StandardTaskEventId)EventId; - return StandardTaskEventId.Unknown; - } - } - - /// - /// Gets the level. This value is null for V1 events. - /// - public string Level { get; internal set; } - - /// - /// Gets the op code. This value is null for V1 events. - /// - public string OpCode { get; internal set; } - - /// - /// Gets the process id. This value is null for V1 events. - /// - public int? ProcessId { get; internal set; } - - /// - /// Gets the record id. This value is null for V1 events. - /// - public long? RecordId { get; internal set; } - - /// - /// Gets the task category. This value is null for V1 events. - /// - public string TaskCategory { get; internal set; } - - /// - /// Gets the task path. - /// - public string TaskPath { get; internal set; } - - /// - /// Gets the time created. - /// - public DateTime? TimeCreated { get; internal set; } - - /// - /// Gets the user id. This value is null for V1 events. - /// - public System.Security.Principal.SecurityIdentifier UserId { get; internal set; } - - /// - /// Gets the version. This value is null for V1 events. - /// - public byte? Version { get; internal set; } - - /// - /// Gets the data value from the task specific event data item list. - /// - /// The name of the data element. - /// Contents of the requested data element if found. null if no value found. - [Obsolete("Use the DataVales property instead.")] - public string GetDataValue(string name) => DataValues?[name]; - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() => EventRecord?.FormatDescription() ?? TaskPath; - - /// - /// Compares the current object with another object of the same type. - /// - /// An object to compare with this object. - /// - /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the other parameter.Zero This object is equal to other. Greater than zero This object is greater than other. - /// - public int CompareTo(TaskEvent other) - { - int i = string.Compare(TaskPath, other.TaskPath, StringComparison.Ordinal); - if (i == 0 && EventRecord != null) - { - i = string.Compare(ActivityId.ToString(), other.ActivityId.ToString(), StringComparison.Ordinal); - if (i == 0) - i = Convert.ToInt32(RecordId - other.RecordId); - } - return i; - } - - /// - /// Get indexer class for data values. - /// - public class EventDataValues - { - private readonly EventLogRecord rec; - - internal EventDataValues(EventLogRecord eventRec) - { - rec = eventRec; - } - - /// - /// Gets the value of the specified property name. - /// - /// - /// The value. - /// - /// Name of the property. - /// Value of the specified property name. null if property does not exist. - public string this[string propertyName] - { - get - { - var propsel = new EventLogPropertySelector(new[] { $"Event/EventData/Data[@Name='{propertyName}']" }); - try - { - var logEventProps = rec.GetPropertyValues(propsel); - return logEventProps[0].ToString(); - } - catch { } - return null; - } - } - } - } - - /// - /// An enumerator over a task's history of events. - /// - public sealed class TaskEventEnumerator : IEnumerator - { - private EventRecord curRec; - private EventLogReader log; - - internal TaskEventEnumerator([NotNull] EventLogReader log) - { - this.log = log; - } - - /// - /// Gets the element in the collection at the current position of the enumerator. - /// - /// - /// The element in the collection at the current position of the enumerator. - /// - public TaskEvent Current => new TaskEvent(curRec); - - /// - /// Gets the element in the collection at the current position of the enumerator. - /// - /// - /// The element in the collection at the current position of the enumerator. - /// - object System.Collections.IEnumerator.Current => Current; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - log.CancelReading(); - log.Dispose(); - log = null; - } - - /// - /// Advances the enumerator to the next element of the collection. - /// - /// - /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. - /// - /// - /// The collection was modified after the enumerator was created. - /// - public bool MoveNext() => (curRec = log.ReadEvent()) != null; - - /// - /// Sets the enumerator to its initial position, which is before the first element in the collection. - /// - /// - /// The collection was modified after the enumerator was created. - /// - public void Reset() - { - log.Seek(System.IO.SeekOrigin.Begin, 0L); - } - - /// - /// Seeks the specified bookmark. - /// - /// The bookmark. - /// The offset. - public void Seek(EventBookmark bookmark, long offset = 0L) - { - log.Seek(bookmark, offset); - } - - /// - /// Seeks the specified origin. - /// - /// The origin. - /// The offset. - public void Seek(System.IO.SeekOrigin origin, long offset) - { - log.Seek(origin, offset); - } - } - - /// - /// Historical event log for a task. Only available for Windows Vista and Windows Server 2008 and later systems. - /// - /// Many applications have the need to audit the execution of the tasks they supply. To enable this, the library provides the TaskEventLog class that allows for TaskEvent instances to be enumerated. This can be done for single tasks or the entire system. It can also be filtered by specific events or criticality. - /// - public sealed class TaskEventLog : IEnumerable - { - private const string TSEventLogPath = "Microsoft-Windows-TaskScheduler/Operational"; - private static readonly bool IsVistaOrLater = Environment.OSVersion.Version.Major >= 6; - - /// - /// Initializes a new instance of the class. - /// - /// The task path. This can be retrieved using the property. - /// Thrown when instantiated on an OS prior to Windows Vista. - public TaskEventLog([CanBeNull] string taskPath) : this(".", taskPath) - { - Initialize(".", BuildQuery(taskPath), true); - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the machine. - /// The task path. This can be retrieved using the property. - /// The domain. - /// The user. - /// The password. - /// Thrown when instantiated on an OS prior to Windows Vista. - public TaskEventLog([NotNull] string machineName, [CanBeNull] string taskPath, string domain = null, string user = null, string password = null) - { - Initialize(machineName, BuildQuery(taskPath), true, domain, user, password); - } - - /// - /// Initializes a new instance of the class that looks at all task events from a specified time. - /// - /// The start time. - /// Name of the task. - /// Name of the machine (optional). - /// The domain. - /// The user. - /// The password. - public TaskEventLog(DateTime startTime, string taskName = null, string machineName = null, string domain = null, string user = null, string password = null) - { - int[] numArray = new int[] { 100, 102, 103, 107, 108, 109, 111, 117, 118, 119, 120, 121, 122, 123, 124, 125 }; - Initialize(machineName, BuildQuery(taskName, numArray, startTime), false, domain, user, password); - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the task. - /// The event ids. - /// The start time. - /// Name of the machine (optional). - /// The domain. - /// The user. - /// The password. - public TaskEventLog(string taskName = null, int[] eventIDs = null, DateTime? startTime = null, string machineName = null, string domain = null, string user = null, string password = null) - { - Initialize(machineName, BuildQuery(taskName, eventIDs, startTime), true, domain, user, password); - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the task. - /// The event ids. - /// The levels. - /// The start time. - /// Name of the machine (optional). - /// The domain. - /// The user. - /// The password. - public TaskEventLog(string taskName = null, int[] eventIDs = null, int[] levels = null, DateTime? startTime = null, string machineName = null, string domain = null, string user = null, string password = null) - { - Initialize(machineName, BuildQuery(taskName, eventIDs, startTime, levels), true, domain, user, password); - } - - internal static string BuildQuery(string taskName = null, int[] eventIDs = null, DateTime? startTime = null, int[] levels = null) - { - const string queryString = - "" + - " " + - " " + - " " + - ""; - const string OR = " or "; - const string AND = " and "; - - System.Text.StringBuilder sb = new System.Text.StringBuilder("*"); - if (eventIDs != null && eventIDs.Length > 0) - { - if (sb.Length > 1) sb.Append(AND); - sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(eventIDs, i => $"EventID={i}"))); - } - if (levels != null && levels.Length > 0) - { - if (sb.Length > 1) sb.Append(AND); - sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(levels, i => $"Level={i}"))); - } - if (startTime.HasValue) - { - if (sb.Length > 1) sb.Append(AND); - sb.AppendFormat("TimeCreated[@SystemTime>='{0}']", System.Xml.XmlConvert.ToString(startTime.Value, System.Xml.XmlDateTimeSerializationMode.RoundtripKind)); - } - if (sb.Length > 1) - { - sb.Insert(1, "[System[Provider[@Name='Microsoft-Windows-TaskScheduler'] and "); - sb.Append(']'); - } - if (!string.IsNullOrEmpty(taskName)) - { - if (sb.Length == 1) - sb.Append('['); - else - sb.Append("]" + AND + "*["); - sb.AppendFormat("EventData[Data[@Name='TaskName']='{0}']", taskName); - } - if (sb.Length > 1) - sb.Append(']'); - return string.Format(queryString, sb); - } - - private void Initialize(string machineName, string query, bool revDir, string domain = null, string user = null, string password = null) - { - if (!IsVistaOrLater) - throw new NotSupportedException("Enumeration of task history not available on systems prior to Windows Vista and Windows Server 2008."); - - System.Security.SecureString spwd = null; - if (password != null) - { - spwd = new System.Security.SecureString(); - foreach (char c in password) - spwd.AppendChar(c); - } - - Query = new EventLogQuery(TSEventLogPath, PathType.LogName, query) { ReverseDirection = revDir }; - if (machineName != null && machineName != "." && !machineName.Equals(Environment.MachineName, StringComparison.InvariantCultureIgnoreCase)) - Query.Session = new EventLogSession(machineName, domain, user, spwd, SessionAuthentication.Default); - } - - /// - /// Gets the total number of events for this task. - /// - public long Count - { - get - { - using (EventLogReader log = new EventLogReader(Query)) - { - long seed = 64L, l = 0L, h = seed; - while (log.ReadEvent() != null) - log.Seek(System.IO.SeekOrigin.Begin, l += seed); - bool foundLast = false; - while (l > 0L && h >= 1L) - { - if (foundLast) - l += (h /= 2L); - else - l -= (h /= 2L); - log.Seek(System.IO.SeekOrigin.Begin, l); - foundLast = (log.ReadEvent() != null); - } - return foundLast ? l + 1L : l; - } - } - } - - /// - /// Gets or sets a value indicating whether this is enabled. - /// - /// - /// true if enabled; otherwise, false. - /// - public bool Enabled - { - get - { - if (!IsVistaOrLater) - return false; - using (var cfg = new EventLogConfiguration(TSEventLogPath, Query.Session)) - return cfg.IsEnabled; - } - set - { - if (!IsVistaOrLater) - throw new NotSupportedException("Task history not available on systems prior to Windows Vista and Windows Server 2008."); - using (var cfg = new EventLogConfiguration(TSEventLogPath, Query.Session)) - { - if (cfg.IsEnabled != value) - { - cfg.IsEnabled = value; - cfg.SaveChanges(); - } - } - } - } - - /// - /// Gets or sets a value indicating whether to enumerate in reverse when calling the default enumerator (typically with foreach statement). - /// - /// - /// true if enumerates in reverse (newest to oldest) by default; otherwise, false to enumerate oldest to newest. - /// - [System.ComponentModel.DefaultValue(false)] - public bool EnumerateInReverse { get; set; } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(EnumerateInReverse); - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// if set to true reverse. - /// - /// A that can be used to iterate through the collection. - /// - [NotNull] - public TaskEventEnumerator GetEnumerator(bool reverse) - { - Query.ReverseDirection = !reverse; - return new TaskEventEnumerator(new EventLogReader(Query)); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(false); - - internal EventLogQuery Query { get; private set; } - } + /// + /// Changes to tasks and the engine that cause events. + /// + public enum StandardTaskEventId + { + /// Task Scheduler started an instance of a task for a user. + /// For detailed information, see the documentation for Event ID 100 on TechNet. + JobStart = 100, + /// Task Scheduler failed to start a task for a user. + /// For detailed information, see the documentation for Event ID 101 on TechNet. + JobStartFailed = 101, + /// Task Scheduler successfully finished an instance of a task for a user. + /// For detailed information, see the documentation for Event ID 102 on TechNet. + JobSuccess = 102, + /// Task Scheduler failed to start an instance of a task for a user. + /// For detailed information, see the documentation for Event ID 103 on TechNet. + JobFailure = 103, + /// Task Scheduler failed to log on the user. + /// For detailed information, see the documentation for Event ID 104 on TechNet. + LogonFailure = 104, + /// Task Scheduler failed to impersonate a user. + /// For detailed information, see the documentation for Event ID 105 on TechNet. + ImpersonationFailure = 105, + /// The a user registered the Task Scheduler a task. + /// For detailed information, see the documentation for Event ID 106 on TechNet. + JobRegistered = 106, + /// Task Scheduler launched an instance of a task due to a time trigger. + /// For detailed information, see the documentation for Event ID 107 on TechNet. + TimeTrigger = 107, + /// Task Scheduler launched an instance of a task due to an event trigger. + /// For detailed information, see the documentation for Event ID 108 on TechNet. + EventTrigger = 108, + /// Task Scheduler launched an instance of a task due to a registration trigger. + /// For detailed information, see the documentation for Event ID 109 on TechNet. + ImmediateTrigger = 109, + /// Task Scheduler launched an instance of a task for a user. + /// For detailed information, see the documentation for Event ID 110 on TechNet. + Run = 110, + /// Task Scheduler terminated an instance of a task due to exceeding the time allocated for execution, as configured in the task definition. + /// For detailed information, see the documentation for Event ID 111 on TechNet. + JobTermination = 111, + /// Task Scheduler could not start a task because the network was unavailable. Ensure the computer is connected to the required network as specified in the task. + /// For detailed information, see the documentation for Event ID 112 on TechNet. + JobNoStartWithoutNetwork = 112, + /// The Task Scheduler registered the a task, but not all the specified triggers will start the task. Ensure all the task triggers are valid. + /// For detailed information, see the documentation for Event ID 113 on TechNet. + TaskRegisteredWithoutSomeTriggers = 113, + /// Task Scheduler could not launch a task as scheduled. Instance is started now as required by the configuration option to start the task when available, if the scheduled time is missed. + /// For detailed information, see the documentation for Event ID 114 on TechNet. + MissedTaskLaunched = 114, + /// Task Scheduler failed to roll back a transaction when updating or deleting a task. + /// For detailed information, see the documentation for Event ID 115 on TechNet. + TransactionRollbackFailure = 115, + /// Task Scheduler saved the configuration for a task, but the credentials used to run the task could not be stored. + /// For detailed information, see the documentation for Event ID 116 on TechNet. + TaskRegisteredWithoutCredentials = 116, + /// Task Scheduler launched an instance of a task due to an idle condition. + /// For detailed information, see the documentation for Event ID 117 on TechNet. + IdleTrigger = 117, + /// Task Scheduler launched an instance of a task due to system startup. + /// For detailed information, see the documentation for Event ID 118 on TechNet. + BootTrigger = 118, + /// Task Scheduler launched an instance of a task due to a user logon. + /// For detailed information, see the documentation for Event ID 119 on TechNet. + LogonTrigger = 119, + /// Task Scheduler launched an instance of a task due to a user connecting to the console. + /// For detailed information, see the documentation for Event ID 120 on TechNet. + ConsoleConnectTrigger = 120, + /// Task Scheduler launched an instance of a task due to a user disconnecting from the console. + /// For detailed information, see the documentation for Event ID 121 on TechNet. + ConsoleDisconnectTrigger = 121, + /// Task Scheduler launched an instance of a task due to a user remotely connecting. + /// For detailed information, see the documentation for Event ID 122 on TechNet. + RemoteConnectTrigger = 122, + /// Task Scheduler launched an instance of a task due to a user remotely disconnecting. + /// For detailed information, see the documentation for Event ID 123 on TechNet. + RemoteDisconnectTrigger = 123, + /// Task Scheduler launched an instance of a task due to a user locking the computer. + /// For detailed information, see the documentation for Event ID 124 on TechNet. + SessionLockTrigger = 124, + /// Task Scheduler launched an instance of a task due to a user unlocking the computer. + /// For detailed information, see the documentation for Event ID 125 on TechNet. + SessionUnlockTrigger = 125, + /// Task Scheduler failed to execute a task. Task Scheduler is attempting to restart the task. + /// For detailed information, see the documentation for Event ID 126 on TechNet. + FailedTaskRestart = 126, + /// Task Scheduler failed to execute a task due to a shutdown race condition. Task Scheduler is attempting to restart the task. + /// For detailed information, see the documentation for Event ID 127 on TechNet. + RejectedTaskRestart = 127, + /// Task Scheduler did not launch a task because the current time exceeds the configured task end time. + /// For detailed information, see the documentation for Event ID 128 on TechNet. + IgnoredTaskStart = 128, + /// Task Scheduler launched an instance of a task in a new process. + /// For detailed information, see the documentation for Event ID 129 on TechNet. + CreatedTaskProcess = 129, + /// The Task Scheduler service failed to start a task due to the service being busy. + /// For detailed information, see the documentation for Event ID 130 on TechNet. + TaskNotRunServiceBusy = 130, + /// Task Scheduler failed to start a task because the number of tasks in the task queue exceeds the quota currently configured. + /// For detailed information, see the documentation for Event ID 131 on TechNet. + TaskNotStartedTaskQueueQuotaExceeded = 131, + /// The Task Scheduler task launching queue quota is approaching its preset limit of tasks currently configured. + /// For detailed information, see the documentation for Event ID 132 on TechNet. + TaskQueueQuotaApproaching = 132, + /// Task Scheduler failed to start a task in the task engine for a user. + /// For detailed information, see the documentation for Event ID 133 on TechNet. + TaskNotStartedEngineQuotaExceeded = 133, + /// Task Engine for a user is approaching its preset limit of tasks. + /// For detailed information, see the documentation for Event ID 134 on TechNet. + EngineQuotaApproaching = 134, + /// Task Scheduler did not launch a task because launch condition not met, machine not idle. + /// For detailed information, see the documentation for Event ID 135 on TechNet. + NotStartedWithoutIdle = 135, + /// A user updated Task Scheduler a task + /// For detailed information, see the documentation for Event ID 140 on TechNet. + TaskUpdated = 140, + /// A user deleted Task Scheduler a task + /// For detailed information, see the documentation for Event ID 141 on TechNet. + TaskDeleted = 141, + /// A user disabled Task Scheduler a task + /// For detailed information, see the documentation for Event ID 142 on TechNet. + TaskDisabled = 142, + /// Task Scheduler woke up the computer to run a task. + /// For detailed information, see the documentation for Event ID 145 on TechNet. + TaskStartedOnComputerWakeup = 145, + /// Task Scheduler failed to subscribe the event trigger for a task. + /// For detailed information, see the documentation for Event ID 150 on TechNet. + TaskEventSubscriptionFailed = 150, + /// Task Scheduler launched an action in an instance of a task. + /// For detailed information, see the documentation for Event ID 200 on TechNet. + ActionStart = 200, + /// Task Scheduler successfully completed a task instance and action. + /// For detailed information, see the documentation for Event ID 201 on TechNet. + ActionSuccess = 201, + /// Task Scheduler failed to complete an instance of a task with an action. + /// For detailed information, see the documentation for Event ID 202 on TechNet. + ActionFailure = 202, + /// Task Scheduler failed to launch an action in a task instance. + /// For detailed information, see the documentation for Event ID 203 on TechNet. + ActionLaunchFailure = 203, + /// Task Scheduler failed to retrieve the event triggering values for a task . The event will be ignored. + /// For detailed information, see the documentation for Event ID 204 on TechNet. + EventRenderFailed = 204, + /// Task Scheduler failed to match the pattern of events for a task. The events will be ignored. + /// For detailed information, see the documentation for Event ID 205 on TechNet. + EventAggregateFailed = 205, + /// Task Scheduler is shutting down the a task engine. + /// For detailed information, see the documentation for Event ID 301 on TechNet. + SessionExit = 301, + /// Task Scheduler is shutting down the a task engine due to an error. + /// For detailed information, see the documentation for Event ID 303 on TechNet. + SessionError = 303, + /// Task Scheduler sent a task to a task engine. + /// For detailed information, see the documentation for Event ID 304 on TechNet. + SessionSentJob = 304, + /// Task Scheduler did not send a task to a task engine. + /// For detailed information, see the documentation for Event ID 305 on TechNet. + SessionSentJobFailed = 305, + /// For a Task Scheduler task engine, the thread pool failed to process the message. + /// For detailed information, see the documentation for Event ID 306 on TechNet. + SessionFailedToProcessMessage = 306, + /// The Task Scheduler service failed to connect to a task engine process. + /// For detailed information, see the documentation for Event ID 307 on TechNet. + SessionManagerConnectFailed = 307, + /// Task Scheduler connected to a task engine process. + /// For detailed information, see the documentation for Event ID 308 on TechNet. + SessionConnected = 308, + /// There are Task Scheduler tasks orphaned during a task engine shutdown. + /// For detailed information, see the documentation for Event ID 309 on TechNet. + SessionJobsOrphaned = 309, + /// Task Scheduler started a task engine process. + /// For detailed information, see the documentation for Event ID 310 on TechNet. + SessionProcessStarted = 310, + /// Task Scheduler failed to start a task engine process due to an error. + /// For detailed information, see the documentation for Event ID 311 on TechNet. + SessionProcessLaunchFailed = 311, + /// Task Scheduler created the Win32 job object for a task engine. + /// For detailed information, see the documentation for Event ID 312 on TechNet. + SessionWin32ObjectCreated = 312, + /// The Task Scheduler channel is ready to send and receive messages. + /// For detailed information, see the documentation for Event ID 313 on TechNet. + SessionChannelReady = 313, + /// Task Scheduler has no tasks running for a task engine, and the idle timer has started. + /// For detailed information, see the documentation for Event ID 314 on TechNet. + SessionIdle = 314, + /// A task engine process failed to connect to the Task Scheduler service. + /// For detailed information, see the documentation for Event ID 315 on TechNet. + SessionProcessConnectFailed = 315, + /// A task engine failed to send a message to the Task Scheduler service. + /// For detailed information, see the documentation for Event ID 316 on TechNet. + SessionMessageSendFailed = 316, + /// Task Scheduler started a task engine process. + /// For detailed information, see the documentation for Event ID 317 on TechNet. + SessionProcessMainStarted = 317, + /// Task Scheduler shut down a task engine process. + /// For detailed information, see the documentation for Event ID 318 on TechNet. + SessionProcessMainShutdown = 318, + /// A task engine received a message from the Task Scheduler service requesting to launch a task. + /// For detailed information, see the documentation for Event ID 319 on TechNet. + SessionProcessReceivedStartJob = 319, + /// A task engine received a message from the Task Scheduler service requesting to stop a task instance. + /// For detailed information, see the documentation for Event ID 320 on TechNet. + SessionProcessReceivedStopJob = 320, + /// Task Scheduler did not launch a task because an instance of the same task is already running. + /// For detailed information, see the documentation for Event ID 322 on TechNet. + NewInstanceIgnored = 322, + /// Task Scheduler stopped an instance of a task in order to launch a new instance. + /// For detailed information, see the documentation for Event ID 323 on TechNet. + RunningInstanceStopped = 323, + /// Task Scheduler queued an instance of a task and will launch it as soon as another instance completes. + /// For detailed information, see the documentation for Event ID 324 on TechNet. + NewInstanceQueued = 324, + /// Task Scheduler queued an instance of a task that will launch immediately. + /// For detailed information, see the documentation for Event ID 325 on TechNet. + InstanceQueued = 325, + /// Task Scheduler did not launch a task because the computer is running on batteries. If launching the task on batteries is required, change the respective flag in the task configuration. + /// For detailed information, see the documentation for Event ID 326 on TechNet. + NoStartOnBatteries = 326, + /// Task Scheduler stopped an instance of a task because the computer is switching to battery power. + /// For detailed information, see the documentation for Event ID 327 on TechNet. + StoppingOnBatteries = 327, + /// Task Scheduler stopped an instance of a task because the computer is no longer idle. + /// For detailed information, see the documentation for Event ID 328 on TechNet. + StoppingOffIdle = 328, + /// Task Scheduler stopped an instance of a task because the task timed out. + /// For detailed information, see the documentation for Event ID 329 on TechNet. + StoppingOnTimeout = 329, + /// Task Scheduler stopped an instance of a task as request by a user . + /// For detailed information, see the documentation for Event ID 330 on TechNet. + StoppingOnRequest = 330, + /// Task Scheduler will continue to execute an instance of a task even after the designated timeout, due to a failure to create the timeout mechanism. + /// For detailed information, see the documentation for Event ID 331 on TechNet. + TimeoutWontWork = 331, + /// Task Scheduler did not launch a task because a user was not logged on when the launching conditions were met. Ensure the user is logged on or change the task definition to allow the task to launch when the user is logged off. + /// For detailed information, see the documentation for Event ID 332 on TechNet. + NoStartUserNotLoggedOn = 332, + /// The Task Scheduler service has started. + /// For detailed information, see the documentation for Event ID 400 on TechNet. + ScheduleServiceStart = 400, + /// The Task Scheduler service failed to start due to an error. + /// For detailed information, see the documentation for Event ID 401 on TechNet. + ScheduleServiceStartFailed = 401, + /// Task Scheduler service is shutting down. + /// For detailed information, see the documentation for Event ID 402 on TechNet. + ScheduleServiceStop = 402, + /// The Task Scheduler service has encountered an error. + /// For detailed information, see the documentation for Event ID 403 on TechNet. + ScheduleServiceError = 403, + /// The Task Scheduler service has encountered an RPC initialization error. + /// For detailed information, see the documentation for Event ID 404 on TechNet. + ScheduleServiceRpcInitError = 404, + /// The Task Scheduler service has failed to initialize COM. + /// For detailed information, see the documentation for Event ID 405 on TechNet. + ScheduleServiceComInitError = 405, + /// The Task Scheduler service failed to initialize the credentials store. + /// For detailed information, see the documentation for Event ID 406 on TechNet. + ScheduleServiceCredStoreInitError = 406, + /// Task Scheduler service failed to initialize LSA. + /// For detailed information, see the documentation for Event ID 407 on TechNet. + ScheduleServiceLsaInitError = 407, + /// Task Scheduler service failed to initialize idle state detection module. Idle tasks may not be started as required. + /// For detailed information, see the documentation for Event ID 408 on TechNet. + ScheduleServiceIdleServiceInitError = 408, + /// The Task Scheduler service failed to initialize a time change notification. System time updates may not be picked by the service and task schedules may not be updated. + /// For detailed information, see the documentation for Event ID 409 on TechNet. + ScheduleServiceTimeChangeInitError = 409, + /// Task Scheduler service received a time system change notification. + /// For detailed information, see the documentation for Event ID 411 on TechNet. + ScheduleServiceTimeChangeSignaled = 411, + /// Task Scheduler service failed to launch tasks triggered by computer startup. Restart the Task Scheduler service. + /// For detailed information, see the documentation for Event ID 412 on TechNet. + ScheduleServiceRunBootJobsFailed = 412, + /// Task Scheduler service started Task Compatibility module. + /// For detailed information, see the documentation for Event ID 700 on TechNet. + CompatStart = 700, + /// Task Scheduler service failed to start Task Compatibility module. Tasks may not be able to register on previous Window versions. + /// For detailed information, see the documentation for Event ID 701 on TechNet. + CompatStartFailed = 701, + /// Task Scheduler failed to initialize the RPC server for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions. + /// For detailed information, see the documentation for Event ID 702 on TechNet. + CompatStartRpcFailed = 702, + /// Task Scheduler failed to initialize Net Schedule API for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions. + /// For detailed information, see the documentation for Event ID 703 on TechNet. + CompatStartNetscheduleFailed = 703, + /// Task Scheduler failed to initialize LSA for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions. + /// For detailed information, see the documentation for Event ID 704 on TechNet. + CompatStartLsaFailed = 704, + /// Task Scheduler failed to start directory monitoring for the Task Compatibility module. + /// For detailed information, see the documentation for Event ID 705 on TechNet. + CompatDirectoryMonitorFailed = 705, + /// Task Compatibility module failed to update a task to the required status. + /// For detailed information, see the documentation for Event ID 706 on TechNet. + CompatTaskStatusUpdateFailed = 706, + /// Task Compatibility module failed to delete a task. + /// For detailed information, see the documentation for Event ID 707 on TechNet. + CompatTaskDeleteFailed = 707, + /// Task Compatibility module failed to set a security descriptor for a task. + /// For detailed information, see the documentation for Event ID 708 on TechNet. + CompatTaskSetSdFailed = 708, + /// Task Compatibility module failed to update a task. + /// For detailed information, see the documentation for Event ID 709 on TechNet. + CompatTaskUpdateFailed = 709, + /// Task Compatibility module failed to upgrade existing tasks. Upgrade will be attempted again next time 'Task Scheduler' service starts. + /// For detailed information, see the documentation for Event ID 710 on TechNet. + CompatUpgradeStartFailed = 710, + /// Task Compatibility module failed to upgrade NetSchedule account. + /// For detailed information, see the documentation for Event ID 711 on TechNet. + CompatUpgradeNsAccountFailed = 711, + /// Task Compatibility module failed to read existing store to upgrade tasks. + /// For detailed information, see the documentation for Event ID 712 on TechNet. + CompatUpgradeStoreEnumFailed = 712, + /// Task Compatibility module failed to load a task for upgrade. + /// For detailed information, see the documentation for Event ID 713 on TechNet. + CompatUpgradeTaskLoadFailed = 713, + /// Task Compatibility module failed to register a task for upgrade. + /// For detailed information, see the documentation for Event ID 714 on TechNet. + CompatUpgradeTaskRegistrationFailed = 714, + /// Task Compatibility module failed to delete LSA store for upgrade. + /// For detailed information, see the documentation for Event ID 715 on TechNet. + CompatUpgradeLsaCleanupFailed = 715, + /// Task Compatibility module failed to upgrade existing scheduled tasks. + /// For detailed information, see the documentation for Event ID 716 on TechNet. + CompatUpgradeFailed = 716, + /// Task Compatibility module failed to determine if upgrade is needed. + /// For detailed information, see the documentation for Event ID 717 on TechNet. + CompatUpgradeNeedNotDetermined = 717, + /// Task scheduler was unable to upgrade the credential store from the Beta 2 version. You may need to re-register any tasks that require passwords. + /// For detailed information, see the documentation for Event ID 718 on TechNet. + VistaBeta2CredstoreUpgradeFailed = 718, + /// A unknown value. + Unknown = -2 + } + + /// + /// Historical event information for a task. This class wraps and extends the class. + /// + /// + /// For events on systems prior to Windows Vista, this class will only have information for the TaskPath, TimeCreated and EventId properties. + /// + [PublicAPI] + public sealed class TaskEvent : IComparable + { + internal TaskEvent([NotNull] EventRecord rec) + { + EventId = rec.Id; + EventRecord = rec; + Version = rec.Version; + TaskCategory = rec.TaskDisplayName; + OpCode = rec.OpcodeDisplayName; + TimeCreated = rec.TimeCreated; + RecordId = rec.RecordId; + ActivityId = rec.ActivityId; + Level = rec.LevelDisplayName; + UserId = rec.UserId; + ProcessId = rec.ProcessId; + TaskPath = rec.Properties.Count > 0 ? rec.Properties[0]?.Value?.ToString() : null; + DataValues = new EventDataValues(rec as EventLogRecord); + } + + internal TaskEvent([NotNull] string taskPath, StandardTaskEventId id, DateTime time) + { + EventId = (int)id; + TaskPath = taskPath; + TimeCreated = time; + } + + /// + /// Gets the activity id. This value is null for V1 events. + /// + public Guid? ActivityId { get; internal set; } + + /// + /// An indexer that gets the value of each of the data item values. This value is null for V1 events. + /// + /// + /// The data values. + /// + public EventDataValues DataValues { get; } + + /// + /// Gets the event id. + /// + public int EventId { get; internal set; } + + /// + /// Gets the underlying . This value is null for V1 events. + /// + public EventRecord EventRecord { get; internal set; } + + /// + /// Gets the from the . + /// + /// + /// The . If not found, returns . + /// + public StandardTaskEventId StandardEventId + { + get + { + if (Enum.IsDefined(typeof(StandardTaskEventId), EventId)) + return (StandardTaskEventId)EventId; + return StandardTaskEventId.Unknown; + } + } + + /// + /// Gets the level. This value is null for V1 events. + /// + public string Level { get; internal set; } + + /// + /// Gets the op code. This value is null for V1 events. + /// + public string OpCode { get; internal set; } + + /// + /// Gets the process id. This value is null for V1 events. + /// + public int? ProcessId { get; internal set; } + + /// + /// Gets the record id. This value is null for V1 events. + /// + public long? RecordId { get; internal set; } + + /// + /// Gets the task category. This value is null for V1 events. + /// + public string TaskCategory { get; internal set; } + + /// + /// Gets the task path. + /// + public string TaskPath { get; internal set; } + + /// + /// Gets the time created. + /// + public DateTime? TimeCreated { get; internal set; } + + /// + /// Gets the user id. This value is null for V1 events. + /// + public System.Security.Principal.SecurityIdentifier UserId { get; internal set; } + + /// + /// Gets the version. This value is null for V1 events. + /// + public byte? Version { get; internal set; } + + /// + /// Gets the data value from the task specific event data item list. + /// + /// The name of the data element. + /// Contents of the requested data element if found. null if no value found. + [Obsolete("Use the DataVales property instead.")] + public string GetDataValue(string name) => DataValues?[name]; + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() => EventRecord?.FormatDescription() ?? TaskPath; + + /// + /// Compares the current object with another object of the same type. + /// + /// An object to compare with this object. + /// + /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the other parameter.Zero This object is equal to other. Greater than zero This object is greater than other. + /// + public int CompareTo(TaskEvent other) + { + int i = string.Compare(TaskPath, other.TaskPath, StringComparison.Ordinal); + if (i == 0 && EventRecord != null) + { + i = string.Compare(ActivityId.ToString(), other.ActivityId.ToString(), StringComparison.Ordinal); + if (i == 0) + i = Convert.ToInt32(RecordId - other.RecordId); + } + return i; + } + + /// + /// Get indexer class for data values. + /// + public class EventDataValues + { + private readonly EventLogRecord rec; + + internal EventDataValues(EventLogRecord eventRec) + { + rec = eventRec; + } + + /// + /// Gets the value of the specified property name. + /// + /// + /// The value. + /// + /// Name of the property. + /// Value of the specified property name. null if property does not exist. + public string this[string propertyName] + { + get + { + var propsel = new EventLogPropertySelector(new[] { $"Event/EventData/Data[@Name='{propertyName}']" }); + try + { + var logEventProps = rec.GetPropertyValues(propsel); + return logEventProps[0].ToString(); + } + catch { } + return null; + } + } + } + } + + /// + /// An enumerator over a task's history of events. + /// + public sealed class TaskEventEnumerator : IEnumerator + { + private EventRecord curRec; + private EventLogReader log; + + internal TaskEventEnumerator([NotNull] EventLogReader log) + { + this.log = log; + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + public TaskEvent Current => new TaskEvent(curRec); + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + object System.Collections.IEnumerator.Current => Current; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + log.CancelReading(); + log.Dispose(); + log = null; + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public bool MoveNext() => (curRec = log.ReadEvent()) != null; + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public void Reset() + { + log.Seek(System.IO.SeekOrigin.Begin, 0L); + } + + /// + /// Seeks the specified bookmark. + /// + /// The bookmark. + /// The offset. + public void Seek(EventBookmark bookmark, long offset = 0L) + { + log.Seek(bookmark, offset); + } + + /// + /// Seeks the specified origin. + /// + /// The origin. + /// The offset. + public void Seek(System.IO.SeekOrigin origin, long offset) + { + log.Seek(origin, offset); + } + } + + /// + /// Historical event log for a task. Only available for Windows Vista and Windows Server 2008 and later systems. + /// + /// Many applications have the need to audit the execution of the tasks they supply. To enable this, the library provides the TaskEventLog class that allows for TaskEvent instances to be enumerated. This can be done for single tasks or the entire system. It can also be filtered by specific events or criticality. + /// + public sealed class TaskEventLog : IEnumerable + { + private const string TSEventLogPath = "Microsoft-Windows-TaskScheduler/Operational"; + private static readonly bool IsVistaOrLater = Environment.OSVersion.Version.Major >= 6; + + /// + /// Initializes a new instance of the class. + /// + /// The task path. This can be retrieved using the property. + /// Thrown when instantiated on an OS prior to Windows Vista. + public TaskEventLog([CanBeNull] string taskPath) : this(".", taskPath) + { + Initialize(".", BuildQuery(taskPath), true); + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the machine. + /// The task path. This can be retrieved using the property. + /// The domain. + /// The user. + /// The password. + /// Thrown when instantiated on an OS prior to Windows Vista. + public TaskEventLog([NotNull] string machineName, [CanBeNull] string taskPath, string domain = null, string user = null, string password = null) + { + Initialize(machineName, BuildQuery(taskPath), true, domain, user, password); + } + + /// + /// Initializes a new instance of the class that looks at all task events from a specified time. + /// + /// The start time. + /// Name of the task. + /// Name of the machine (optional). + /// The domain. + /// The user. + /// The password. + public TaskEventLog(DateTime startTime, string taskName = null, string machineName = null, string domain = null, string user = null, string password = null) + { + int[] numArray = new int[] { 100, 102, 103, 107, 108, 109, 111, 117, 118, 119, 120, 121, 122, 123, 124, 125 }; + Initialize(machineName, BuildQuery(taskName, numArray, startTime), false, domain, user, password); + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the task. + /// The event ids. + /// The start time. + /// Name of the machine (optional). + /// The domain. + /// The user. + /// The password. + public TaskEventLog(string taskName = null, int[] eventIDs = null, DateTime? startTime = null, string machineName = null, string domain = null, string user = null, string password = null) + { + Initialize(machineName, BuildQuery(taskName, eventIDs, startTime), true, domain, user, password); + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the task. + /// The event ids. + /// The levels. + /// The start time. + /// Name of the machine (optional). + /// The domain. + /// The user. + /// The password. + public TaskEventLog(string taskName = null, int[] eventIDs = null, int[] levels = null, DateTime? startTime = null, string machineName = null, string domain = null, string user = null, string password = null) + { + Initialize(machineName, BuildQuery(taskName, eventIDs, startTime, levels), true, domain, user, password); + } + + internal static string BuildQuery(string taskName = null, int[] eventIDs = null, DateTime? startTime = null, int[] levels = null) + { + const string queryString = + "" + + " " + + " " + + " " + + ""; + const string OR = " or "; + const string AND = " and "; + + System.Text.StringBuilder sb = new System.Text.StringBuilder("*"); + if (eventIDs != null && eventIDs.Length > 0) + { + if (sb.Length > 1) sb.Append(AND); + sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(eventIDs, i => $"EventID={i}"))); + } + if (levels != null && levels.Length > 0) + { + if (sb.Length > 1) sb.Append(AND); + sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(levels, i => $"Level={i}"))); + } + if (startTime.HasValue) + { + if (sb.Length > 1) sb.Append(AND); + sb.AppendFormat("TimeCreated[@SystemTime>='{0}']", System.Xml.XmlConvert.ToString(startTime.Value, System.Xml.XmlDateTimeSerializationMode.RoundtripKind)); + } + if (sb.Length > 1) + { + sb.Insert(1, "[System[Provider[@Name='Microsoft-Windows-TaskScheduler'] and "); + sb.Append(']'); + } + if (!string.IsNullOrEmpty(taskName)) + { + if (sb.Length == 1) + sb.Append('['); + else + sb.Append("]" + AND + "*["); + sb.AppendFormat("EventData[Data[@Name='TaskName']='{0}']", taskName); + } + if (sb.Length > 1) + sb.Append(']'); + return string.Format(queryString, sb); + } + + private void Initialize(string machineName, string query, bool revDir, string domain = null, string user = null, string password = null) + { + if (!IsVistaOrLater) + throw new NotSupportedException("Enumeration of task history not available on systems prior to Windows Vista and Windows Server 2008."); + + System.Security.SecureString spwd = null; + if (password != null) + { + spwd = new System.Security.SecureString(); + foreach (char c in password) + spwd.AppendChar(c); + } + + Query = new EventLogQuery(TSEventLogPath, PathType.LogName, query) { ReverseDirection = revDir }; + if (machineName != null && machineName != "." && !machineName.Equals(Environment.MachineName, StringComparison.InvariantCultureIgnoreCase)) + Query.Session = new EventLogSession(machineName, domain, user, spwd, SessionAuthentication.Default); + } + + /// + /// Gets the total number of events for this task. + /// + public long Count + { + get + { + using (EventLogReader log = new EventLogReader(Query)) + { + long seed = 64L, l = 0L, h = seed; + while (log.ReadEvent() != null) + log.Seek(System.IO.SeekOrigin.Begin, l += seed); + bool foundLast = false; + while (l > 0L && h >= 1L) + { + if (foundLast) + l += (h /= 2L); + else + l -= (h /= 2L); + log.Seek(System.IO.SeekOrigin.Begin, l); + foundLast = (log.ReadEvent() != null); + } + return foundLast ? l + 1L : l; + } + } + } + + /// + /// Gets or sets a value indicating whether this is enabled. + /// + /// + /// true if enabled; otherwise, false. + /// + public bool Enabled + { + get + { + if (!IsVistaOrLater) + return false; + using (var cfg = new EventLogConfiguration(TSEventLogPath, Query.Session)) + return cfg.IsEnabled; + } + set + { + if (!IsVistaOrLater) + throw new NotSupportedException("Task history not available on systems prior to Windows Vista and Windows Server 2008."); + using (var cfg = new EventLogConfiguration(TSEventLogPath, Query.Session)) + { + if (cfg.IsEnabled != value) + { + cfg.IsEnabled = value; + cfg.SaveChanges(); + } + } + } + } + + /// + /// Gets or sets a value indicating whether to enumerate in reverse when calling the default enumerator (typically with foreach statement). + /// + /// + /// true if enumerates in reverse (newest to oldest) by default; otherwise, false to enumerate oldest to newest. + /// + [System.ComponentModel.DefaultValue(false)] + public bool EnumerateInReverse { get; set; } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(EnumerateInReverse); + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// if set to true reverse. + /// + /// A that can be used to iterate through the collection. + /// + [NotNull] + public TaskEventEnumerator GetEnumerator(bool reverse) + { + Query.ReverseDirection = !reverse; + return new TaskEventEnumerator(new EventLogReader(Query)); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(false); + + internal EventLogQuery Query { get; private set; } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolder.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolder.cs index 2bc780306..b6ef4c8a6 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolder.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolder.cs @@ -1,636 +1,633 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Security; using System.Security.AccessControl; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using winPEAS.TaskScheduler.V1; using winPEAS.TaskScheduler.V2; namespace winPEAS.TaskScheduler { - /// - /// Provides the methods that are used to register (create) tasks in the folder, remove tasks from the folder, and create or remove subfolders from the folder. - /// - [PublicAPI] - public sealed class TaskFolder : IDisposable, IComparable - { - private ITaskScheduler v1List; - private readonly ITaskFolder v2Folder; - - internal const string rootString = @"\"; - - internal TaskFolder([NotNull] TaskService svc) - { - TaskService = svc; - v1List = svc.v1TaskScheduler; - } - - internal TaskFolder([NotNull] TaskService svc, [NotNull] ITaskFolder iFldr) - { - TaskService = svc; - v2Folder = iFldr; - } - - /// - /// Releases all resources used by this class. - /// - public void Dispose() - { - if (v2Folder != null) - Marshal.ReleaseComObject(v2Folder); - v1List = null; - } - - /// - /// Gets a which enumerates all the tasks in this and all subfolders. - /// - /// - /// A for all instances. - /// - [NotNull, ItemNotNull] - public IEnumerable AllTasks => EnumerateFolderTasks(this); - - /// - /// Gets the name that is used to identify the folder that contains a task. - /// - [NotNull] - public string Name => (v2Folder == null) ? rootString : v2Folder.Name; - - /// - /// Gets the parent folder of this folder. - /// - /// - /// The parent folder, or null if this folder is the root folder. - /// - public TaskFolder Parent - { - get - { - // V1 only has the root folder - if (v2Folder == null) - return null; - - string path = v2Folder.Path; - string parentPath = System.IO.Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(parentPath)) - return null; - return TaskService.GetFolder(parentPath); - } - } - - /// - /// Gets the path to where the folder is stored. - /// - [NotNull] - public string Path => (v2Folder == null) ? rootString : v2Folder.Path; - - [NotNull] - internal TaskFolder GetFolder([NotNull] string path) - { - if (v2Folder != null) - return new TaskFolder(TaskService, v2Folder.GetFolder(path)); - throw new NotV1SupportedException(); - } - - /// - /// Gets or sets the security descriptor of the task. - /// - /// The security descriptor. - [Obsolete("This property will be removed in deference to the GetAccessControl, GetSecurityDescriptorSddlForm, SetAccessControl and SetSecurityDescriptorSddlForm methods.")] - public GenericSecurityDescriptor SecurityDescriptor - { + /// + /// Provides the methods that are used to register (create) tasks in the folder, remove tasks from the folder, and create or remove subfolders from the folder. + /// + [PublicAPI] + public sealed class TaskFolder : IDisposable, IComparable + { + private ITaskScheduler v1List; + private readonly ITaskFolder v2Folder; + + internal const string rootString = @"\"; + + internal TaskFolder([NotNull] TaskService svc) + { + TaskService = svc; + v1List = svc.v1TaskScheduler; + } + + internal TaskFolder([NotNull] TaskService svc, [NotNull] ITaskFolder iFldr) + { + TaskService = svc; + v2Folder = iFldr; + } + + /// + /// Releases all resources used by this class. + /// + public void Dispose() + { + if (v2Folder != null) + Marshal.ReleaseComObject(v2Folder); + v1List = null; + } + + /// + /// Gets a which enumerates all the tasks in this and all subfolders. + /// + /// + /// A for all instances. + /// + [NotNull, ItemNotNull] + public IEnumerable AllTasks => EnumerateFolderTasks(this); + + /// + /// Gets the name that is used to identify the folder that contains a task. + /// + [NotNull] + public string Name => (v2Folder == null) ? rootString : v2Folder.Name; + + /// + /// Gets the parent folder of this folder. + /// + /// + /// The parent folder, or null if this folder is the root folder. + /// + public TaskFolder Parent + { + get + { + // V1 only has the root folder + if (v2Folder == null) + return null; + + string path = v2Folder.Path; + string parentPath = System.IO.Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(parentPath)) + return null; + return TaskService.GetFolder(parentPath); + } + } + + /// + /// Gets the path to where the folder is stored. + /// + [NotNull] + public string Path => (v2Folder == null) ? rootString : v2Folder.Path; + + [NotNull] + internal TaskFolder GetFolder([NotNull] string path) + { + if (v2Folder != null) + return new TaskFolder(TaskService, v2Folder.GetFolder(path)); + throw new NotV1SupportedException(); + } + + /// + /// Gets or sets the security descriptor of the task. + /// + /// The security descriptor. + [Obsolete("This property will be removed in deference to the GetAccessControl, GetSecurityDescriptorSddlForm, SetAccessControl and SetSecurityDescriptorSddlForm methods.")] + public GenericSecurityDescriptor SecurityDescriptor + { #pragma warning disable 0618 - get { return GetSecurityDescriptor(); } - set { SetSecurityDescriptor(value); } + get { return GetSecurityDescriptor(); } + set { SetSecurityDescriptor(value); } #pragma warning restore 0618 - } - - /// - /// Gets all the subfolders in the folder. - /// - [NotNull, ItemNotNull] - public TaskFolderCollection SubFolders - { - get - { - try - { - if (v2Folder != null) - return new TaskFolderCollection(this, v2Folder.GetFolders(0)); - } - catch { } - return new TaskFolderCollection(); - } - } - - /// - /// Gets a collection of all the tasks in the folder. - /// - [NotNull, ItemNotNull] - public TaskCollection Tasks => GetTasks(); - - /// - /// Gets or sets the that manages this task. - /// - /// The task service. - public TaskService TaskService { get; } - - /// - /// Compares the current object with another object of the same type. - /// - /// An object to compare with this object. - /// - /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the parameter.Zero This object is equal to . Greater than zero This object is greater than . - /// - int IComparable.CompareTo(TaskFolder other) => string.Compare(Path, other.Path, true); - - /// - /// Creates a folder for related tasks. Not available to Task Scheduler 1.0. - /// - /// The name used to identify the folder. If "FolderName\SubFolder1\SubFolder2" is specified, the entire folder tree will be created if the folders do not exist. This parameter can be a relative path to the current instance. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// The security descriptor associated with the folder. - /// A instance that represents the new subfolder. - [Obsolete("This method will be removed in deference to the CreateFolder(string, TaskSecurity) method.")] - public TaskFolder CreateFolder([NotNull] string subFolderName, GenericSecurityDescriptor sd) => CreateFolder(subFolderName, sd == null ? null : sd.GetSddlForm(Task.defaultAccessControlSections)); - - /// - /// Creates a folder for related tasks. Not available to Task Scheduler 1.0. - /// - /// The name used to identify the folder. If "FolderName\SubFolder1\SubFolder2" is specified, the entire folder tree will be created if the folders do not exist. This parameter can be a relative path to the current instance. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// The task security associated with the folder. - /// A instance that represents the new subfolder. - public TaskFolder CreateFolder([NotNull] string subFolderName, [NotNull] TaskSecurity folderSecurity) - { - if (folderSecurity == null) - throw new ArgumentNullException(nameof(folderSecurity)); - return CreateFolder(subFolderName, folderSecurity.GetSecurityDescriptorSddlForm(Task.defaultAccessControlSections)); - } - - /// - /// Creates a folder for related tasks. Not available to Task Scheduler 1.0. - /// - /// The name used to identify the folder. If "FolderName\SubFolder1\SubFolder2" is specified, the entire folder tree will be created if the folders do not exist. This parameter can be a relative path to the current instance. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// The security descriptor associated with the folder. - /// Set this value to false to avoid having an exception called if the folder already exists. - /// A instance that represents the new subfolder. - /// Security descriptor mismatch between specified credentials and credentials on existing folder by same name. - /// Invalid SDDL form. - /// Not supported under Task Scheduler 1.0. - public TaskFolder CreateFolder([NotNull] string subFolderName, string sddlForm = null, bool exceptionOnExists = true) - { - if (v2Folder == null) throw new NotV1SupportedException(); - ITaskFolder ifld = null; - try { ifld = v2Folder.CreateFolder(subFolderName, sddlForm); } - catch (COMException ce) - { - int serr = ce.ErrorCode & 0x0000FFFF; - if (serr == 0xb7) // ERROR_ALREADY_EXISTS - { - if (exceptionOnExists) throw; - try - { - ifld = v2Folder.GetFolder(subFolderName); - if (ifld != null && sddlForm != null && sddlForm.Trim().Length > 0) - { - string sd = ifld.GetSecurityDescriptor((int)Task.defaultSecurityInfosSections); - if (string.Compare(sddlForm, sd, StringComparison.OrdinalIgnoreCase) != 0) - throw new SecurityException("Security descriptor mismatch between specified credentials and credentials on existing folder by same name."); - } - } - catch - { - if (ifld != null) - Marshal.ReleaseComObject(ifld); - throw; - } - } - else if (serr == 0x534 || serr == 0x538 || serr == 0x539 || serr == 0x53A || serr == 0x519 || serr == 0x57) - throw new ArgumentException(@"Invalid SDDL form", nameof(sddlForm), ce); - else - throw; - } - return new TaskFolder(TaskService, ifld); - } - - /// - /// Deletes a subfolder from the parent folder. Not available to Task Scheduler 1.0. - /// - /// The name of the subfolder to be removed. The root task folder is specified with a backslash (\). This parameter can be a relative path to the folder you want to delete. An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// Set this value to false to avoid having an exception called if the folder does not exist. - /// Not supported under Task Scheduler 1.0. - public void DeleteFolder([NotNull] string subFolderName, bool exceptionOnNotExists = true) - { - if (v2Folder != null) - { - try - { - v2Folder.DeleteFolder(subFolderName, 0); - } - catch (Exception e) - { - if (!(e is FileNotFoundException || e is DirectoryNotFoundException) || exceptionOnNotExists) - throw; - } - } - else - throw new NotV1SupportedException(); - } - - /// Deletes a task from the folder. - /// - /// The name of the task that is specified when the task was registered. The '.' character cannot be used to specify the current task folder and the '..' - /// characters cannot be used to specify the parent task folder in the path. - /// - /// Set this value to false to avoid having an exception called if the task does not exist. - public void DeleteTask([NotNull] string name, bool exceptionOnNotExists = true) - { - try - { - if (v2Folder != null) - v2Folder.DeleteTask(name, 0); - else - { - if (!name.EndsWith(".job", StringComparison.CurrentCultureIgnoreCase)) - name += ".job"; - v1List.Delete(name); - } - } - catch (FileNotFoundException) - { - if (exceptionOnNotExists) - throw; - } - } - - /// Returns an enumerable collection of folders that matches a specified filter and recursion option. - /// An optional predicate used to filter the returned instances. - /// An enumerable collection of folders that matches . - [NotNull, ItemNotNull] - public IEnumerable EnumerateFolders(Predicate filter = null) - { - foreach (var fld in SubFolders) - { - if (filter == null || filter(fld)) - yield return fld; - } - } - - /// Returns an enumerable collection of tasks that matches a specified filter and recursion option. - /// An optional predicate used to filter the returned instances. - /// Specifies whether the enumeration should include tasks in any subfolders. - /// An enumerable collection of directories that matches and . - [NotNull, ItemNotNull] - public IEnumerable EnumerateTasks(Predicate filter = null, bool recurse = false) => EnumerateFolderTasks(this, filter, recurse); - - /// Determines whether the specified , is equal to this instance. - /// The to compare with this instance. - /// true if the specified is equal to this instance; otherwise, false. - public override bool Equals(object obj) - { - var folder = obj as TaskFolder; - if (folder != null) - return Path == folder.Path && TaskService.TargetServer == folder.TaskService.TargetServer && GetSecurityDescriptorSddlForm() == folder.GetSecurityDescriptorSddlForm(); - return false; - } - - /// - /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task described by the - /// current object. - /// - /// A object that encapsulates the access control rules for the current folder. - [NotNull] - public TaskSecurity GetAccessControl() => GetAccessControl(Task.defaultAccessControlSections); - - /// - /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task folder described by - /// the current object. - /// - /// - /// One of the values that specifies which group of access control entries to retrieve. - /// - /// A object that encapsulates the access control rules for the current folder. - [NotNull] - public TaskSecurity GetAccessControl(AccessControlSections includeSections) => new TaskSecurity(this, includeSections); - - /// Returns a hash code for this instance. - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() => new { A = Path, B = TaskService.TargetServer, C = GetSecurityDescriptorSddlForm() }.GetHashCode(); - - /// Gets the security descriptor for the folder. Not available to Task Scheduler 1.0. - /// Section(s) of the security descriptor to return. - /// The security descriptor for the folder. - [Obsolete("This method will be removed in deference to the GetAccessControl and GetSecurityDescriptorSddlForm methods.")] - public GenericSecurityDescriptor GetSecurityDescriptor(SecurityInfos includeSections = Task.defaultSecurityInfosSections) => new RawSecurityDescriptor(GetSecurityDescriptorSddlForm(includeSections)); - - /// - /// Gets the security descriptor for the folder. Not available to Task Scheduler 1.0. - /// - /// Section(s) of the security descriptor to return. - /// The security descriptor for the folder. - /// Not supported under Task Scheduler 1.0. - public string GetSecurityDescriptorSddlForm(SecurityInfos includeSections = Task.defaultSecurityInfosSections) - { - if (v2Folder != null) - return v2Folder.GetSecurityDescriptor((int)includeSections); - throw new NotV1SupportedException(); - } - - /// - /// Gets a collection of all the tasks in the folder whose name matches the optional . - /// - /// The optional name filter expression. - /// Collection of all matching tasks. - [NotNull, ItemNotNull] - public TaskCollection GetTasks(Regex filter = null) - { - if (v2Folder != null) - return new TaskCollection(this, v2Folder.GetTasks(1), filter); - return new TaskCollection(TaskService, filter); - } - - /// Imports a from an XML file. - /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// The file containing the XML-formatted definition of the task. - /// If set to , overwrites any existing task with the same name. - /// A instance that represents the new task. - /// Importing from an XML file is only supported under Task Scheduler 2.0. - public Task ImportTask(string path, [NotNull] string xmlFile, bool overwriteExisting = true) => RegisterTask(path, File.ReadAllText(xmlFile), overwriteExisting ? TaskCreation.CreateOrUpdate : TaskCreation.Create); - - /// - /// Registers (creates) a new task in the folder using XML to define the task. - /// - /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// An XML-formatted definition of the task. - /// A union of flags. - /// The user credentials used to register the task. - /// The password for the userId used to register the task. - /// A value that defines what logon technique is used to run the registered task. - /// The security descriptor associated with the registered task. You can specify the access control list (ACL) in the security descriptor for a task in order to allow or deny certain users and groups access to a task. - /// A instance that represents the new task. - /// " + - /// "" + - /// " " + - /// " " + - /// " S-1-5-18" + - /// " " + - /// " " + - /// " " + - /// " " + - /// " 2017-09-04T14:04:03" + - /// " " + - /// " " + - /// " " + - /// " " + - /// " " + - /// " cmd" + - /// " " + - /// " " + - /// ""; - /// // Register the task in the root folder of the local machine using the SYSTEM account defined in XML - /// TaskService.Instance.RootFolder.RegisterTaskDefinition("Test", xml); - /// ]]> - public Task RegisterTask(string path, [NotNull] string xmlText, TaskCreation createType = TaskCreation.CreateOrUpdate, string userId = null, string password = null, TaskLogonType logonType = TaskLogonType.S4U, string sddl = null) - { - if (v2Folder != null) - return Task.CreateTask(TaskService, v2Folder.RegisterTask(path, xmlText, (int)createType, userId, password, logonType, sddl)); - - TaskDefinition td = TaskService.NewTask(); - XmlSerializationHelper.ReadObjectFromXmlText(xmlText, td); - return RegisterTaskDefinition(path, td, createType, userId ?? td.Principal.ToString(), - password, logonType == TaskLogonType.S4U ? td.Principal.LogonType : logonType, sddl); - } - - /// - /// Registers (creates) a task in a specified location using a instance to define a task. - /// - /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// The of the registered task. - /// A instance that represents the new task. - /// - /// - public Task RegisterTaskDefinition(string path, [NotNull] TaskDefinition definition) => RegisterTaskDefinition(path, definition, TaskCreation.CreateOrUpdate, - definition.Principal.ToString(), null, definition.Principal.LogonType); - - /// - /// Registers (creates) a task in a specified location using a instance to define a task. - /// - /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// The of the registered task. - /// A union of flags. - /// The user credentials used to register the task. - /// The password for the userId used to register the task. - /// A value that defines what logon technique is used to run the registered task. - /// The security descriptor associated with the registered task. You can specify the access control list (ACL) in the security descriptor for a task in order to allow or deny certain users and groups access to a task. - /// - /// A instance that represents the new task. This will return null if is set to ValidateOnly and there are no validation errors. - /// - /// - /// Task names may not include any characters which are invalid for file names. - /// or - /// Task names ending with a period followed by three or fewer characters cannot be retrieved due to a bug in the native library. - /// - /// This LogonType is not supported on Task Scheduler 1.0. - /// or - /// Security settings are not available on Task Scheduler 1.0. - /// or - /// Registration triggers are not available on Task Scheduler 1.0. - /// or - /// XML validation not available on Task Scheduler 1.0. - /// This method is effectively the "Save" method for tasks. It takes a modified TaskDefinition instance and registers it in the folder defined by this TaskFolder instance. Optionally, you can use this method to override the user, password and logon type defined in the definition and supply security against the task. - /// - /// This first example registers a simple task with a single trigger and action using the default security. - /// - /// This example registers that same task using the SYSTEM account. - /// - /// This example registers that same task using a specific username and password along with a security definition. - /// - public Task RegisterTaskDefinition([NotNull] string path, [NotNull] TaskDefinition definition, TaskCreation createType, string userId, string password = null, TaskLogonType logonType = TaskLogonType.S4U, string sddl = null) - { - if (definition.Actions.Count < 1 || definition.Actions.Count > 32) - throw new ArgumentOutOfRangeException(nameof(definition.Actions), @"A task must be registered with at least one action and no more than 32 actions."); - - userId ??= definition.Principal.Account; - if (userId == string.Empty) userId = null; - User user = new User(userId); - if (v2Folder != null) - { - definition.Actions.ConvertUnsupportedActions(); - if (logonType == TaskLogonType.ServiceAccount) - { - if (string.IsNullOrEmpty(userId) || !user.IsServiceAccount) - throw new ArgumentException(@"A valid system account name must be supplied for TaskLogonType.ServiceAccount. Valid entries are ""NT AUTHORITY\SYSTEM"", ""SYSTEM"", ""NT AUTHORITY\LOCALSERVICE"", or ""NT AUTHORITY\NETWORKSERVICE"".", nameof(userId)); - if (password != null) - throw new ArgumentException(@"A password cannot be supplied when specifying TaskLogonType.ServiceAccount.", nameof(password)); - } - /*else if ((LogonType == TaskLogonType.Password || LogonType == TaskLogonType.InteractiveTokenOrPassword || + } + + /// + /// Gets all the subfolders in the folder. + /// + [NotNull, ItemNotNull] + public TaskFolderCollection SubFolders + { + get + { + try + { + if (v2Folder != null) + return new TaskFolderCollection(this, v2Folder.GetFolders(0)); + } + catch { } + return new TaskFolderCollection(); + } + } + + /// + /// Gets a collection of all the tasks in the folder. + /// + [NotNull, ItemNotNull] + public TaskCollection Tasks => GetTasks(); + + /// + /// Gets or sets the that manages this task. + /// + /// The task service. + public TaskService TaskService { get; } + + /// + /// Compares the current object with another object of the same type. + /// + /// An object to compare with this object. + /// + /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the parameter.Zero This object is equal to . Greater than zero This object is greater than . + /// + int IComparable.CompareTo(TaskFolder other) => string.Compare(Path, other.Path, true); + + /// + /// Creates a folder for related tasks. Not available to Task Scheduler 1.0. + /// + /// The name used to identify the folder. If "FolderName\SubFolder1\SubFolder2" is specified, the entire folder tree will be created if the folders do not exist. This parameter can be a relative path to the current instance. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// The security descriptor associated with the folder. + /// A instance that represents the new subfolder. + [Obsolete("This method will be removed in deference to the CreateFolder(string, TaskSecurity) method.")] + public TaskFolder CreateFolder([NotNull] string subFolderName, GenericSecurityDescriptor sd) => CreateFolder(subFolderName, sd == null ? null : sd.GetSddlForm(Task.defaultAccessControlSections)); + + /// + /// Creates a folder for related tasks. Not available to Task Scheduler 1.0. + /// + /// The name used to identify the folder. If "FolderName\SubFolder1\SubFolder2" is specified, the entire folder tree will be created if the folders do not exist. This parameter can be a relative path to the current instance. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// The task security associated with the folder. + /// A instance that represents the new subfolder. + public TaskFolder CreateFolder([NotNull] string subFolderName, [NotNull] TaskSecurity folderSecurity) + { + if (folderSecurity == null) + throw new ArgumentNullException(nameof(folderSecurity)); + return CreateFolder(subFolderName, folderSecurity.GetSecurityDescriptorSddlForm(Task.defaultAccessControlSections)); + } + + /// + /// Creates a folder for related tasks. Not available to Task Scheduler 1.0. + /// + /// The name used to identify the folder. If "FolderName\SubFolder1\SubFolder2" is specified, the entire folder tree will be created if the folders do not exist. This parameter can be a relative path to the current instance. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// The security descriptor associated with the folder. + /// Set this value to false to avoid having an exception called if the folder already exists. + /// A instance that represents the new subfolder. + /// Security descriptor mismatch between specified credentials and credentials on existing folder by same name. + /// Invalid SDDL form. + /// Not supported under Task Scheduler 1.0. + public TaskFolder CreateFolder([NotNull] string subFolderName, string sddlForm = null, bool exceptionOnExists = true) + { + if (v2Folder == null) throw new NotV1SupportedException(); + ITaskFolder ifld = null; + try { ifld = v2Folder.CreateFolder(subFolderName, sddlForm); } + catch (COMException ce) + { + int serr = ce.ErrorCode & 0x0000FFFF; + if (serr == 0xb7) // ERROR_ALREADY_EXISTS + { + if (exceptionOnExists) throw; + try + { + ifld = v2Folder.GetFolder(subFolderName); + if (ifld != null && sddlForm != null && sddlForm.Trim().Length > 0) + { + string sd = ifld.GetSecurityDescriptor((int)Task.defaultSecurityInfosSections); + if (string.Compare(sddlForm, sd, StringComparison.OrdinalIgnoreCase) != 0) + throw new SecurityException("Security descriptor mismatch between specified credentials and credentials on existing folder by same name."); + } + } + catch + { + if (ifld != null) + Marshal.ReleaseComObject(ifld); + throw; + } + } + else if (serr == 0x534 || serr == 0x538 || serr == 0x539 || serr == 0x53A || serr == 0x519 || serr == 0x57) + throw new ArgumentException(@"Invalid SDDL form", nameof(sddlForm), ce); + else + throw; + } + return new TaskFolder(TaskService, ifld); + } + + /// + /// Deletes a subfolder from the parent folder. Not available to Task Scheduler 1.0. + /// + /// The name of the subfolder to be removed. The root task folder is specified with a backslash (\). This parameter can be a relative path to the folder you want to delete. An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// Set this value to false to avoid having an exception called if the folder does not exist. + /// Not supported under Task Scheduler 1.0. + public void DeleteFolder([NotNull] string subFolderName, bool exceptionOnNotExists = true) + { + if (v2Folder != null) + { + try + { + v2Folder.DeleteFolder(subFolderName, 0); + } + catch (Exception e) + { + if (!(e is FileNotFoundException || e is DirectoryNotFoundException) || exceptionOnNotExists) + throw; + } + } + else + throw new NotV1SupportedException(); + } + + /// Deletes a task from the folder. + /// + /// The name of the task that is specified when the task was registered. The '.' character cannot be used to specify the current task folder and the '..' + /// characters cannot be used to specify the parent task folder in the path. + /// + /// Set this value to false to avoid having an exception called if the task does not exist. + public void DeleteTask([NotNull] string name, bool exceptionOnNotExists = true) + { + try + { + if (v2Folder != null) + v2Folder.DeleteTask(name, 0); + else + { + if (!name.EndsWith(".job", StringComparison.CurrentCultureIgnoreCase)) + name += ".job"; + v1List.Delete(name); + } + } + catch (FileNotFoundException) + { + if (exceptionOnNotExists) + throw; + } + } + + /// Returns an enumerable collection of folders that matches a specified filter and recursion option. + /// An optional predicate used to filter the returned instances. + /// An enumerable collection of folders that matches . + [NotNull, ItemNotNull] + public IEnumerable EnumerateFolders(Predicate filter = null) + { + foreach (var fld in SubFolders) + { + if (filter == null || filter(fld)) + yield return fld; + } + } + + /// Returns an enumerable collection of tasks that matches a specified filter and recursion option. + /// An optional predicate used to filter the returned instances. + /// Specifies whether the enumeration should include tasks in any subfolders. + /// An enumerable collection of directories that matches and . + [NotNull, ItemNotNull] + public IEnumerable EnumerateTasks(Predicate filter = null, bool recurse = false) => EnumerateFolderTasks(this, filter, recurse); + + /// Determines whether the specified , is equal to this instance. + /// The to compare with this instance. + /// true if the specified is equal to this instance; otherwise, false. + public override bool Equals(object obj) + { + var folder = obj as TaskFolder; + if (folder != null) + return Path == folder.Path && TaskService.TargetServer == folder.TaskService.TargetServer && GetSecurityDescriptorSddlForm() == folder.GetSecurityDescriptorSddlForm(); + return false; + } + + /// + /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task described by the + /// current object. + /// + /// A object that encapsulates the access control rules for the current folder. + [NotNull] + public TaskSecurity GetAccessControl() => GetAccessControl(Task.defaultAccessControlSections); + + /// + /// Gets a object that encapsulates the specified type of access control list (ACL) entries for the task folder described by + /// the current object. + /// + /// + /// One of the values that specifies which group of access control entries to retrieve. + /// + /// A object that encapsulates the access control rules for the current folder. + [NotNull] + public TaskSecurity GetAccessControl(AccessControlSections includeSections) => new TaskSecurity(this, includeSections); + + /// Returns a hash code for this instance. + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + public override int GetHashCode() => new { A = Path, B = TaskService.TargetServer, C = GetSecurityDescriptorSddlForm() }.GetHashCode(); + + /// Gets the security descriptor for the folder. Not available to Task Scheduler 1.0. + /// Section(s) of the security descriptor to return. + /// The security descriptor for the folder. + [Obsolete("This method will be removed in deference to the GetAccessControl and GetSecurityDescriptorSddlForm methods.")] + public GenericSecurityDescriptor GetSecurityDescriptor(SecurityInfos includeSections = Task.defaultSecurityInfosSections) => new RawSecurityDescriptor(GetSecurityDescriptorSddlForm(includeSections)); + + /// + /// Gets the security descriptor for the folder. Not available to Task Scheduler 1.0. + /// + /// Section(s) of the security descriptor to return. + /// The security descriptor for the folder. + /// Not supported under Task Scheduler 1.0. + public string GetSecurityDescriptorSddlForm(SecurityInfos includeSections = Task.defaultSecurityInfosSections) + { + if (v2Folder != null) + return v2Folder.GetSecurityDescriptor((int)includeSections); + throw new NotV1SupportedException(); + } + + /// + /// Gets a collection of all the tasks in the folder whose name matches the optional . + /// + /// The optional name filter expression. + /// Collection of all matching tasks. + [NotNull, ItemNotNull] + public TaskCollection GetTasks(Regex filter = null) + { + if (v2Folder != null) + return new TaskCollection(this, v2Folder.GetTasks(1), filter); + return new TaskCollection(TaskService, filter); + } + + /// Imports a from an XML file. + /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// The file containing the XML-formatted definition of the task. + /// If set to , overwrites any existing task with the same name. + /// A instance that represents the new task. + /// Importing from an XML file is only supported under Task Scheduler 2.0. + public Task ImportTask(string path, [NotNull] string xmlFile, bool overwriteExisting = true) => RegisterTask(path, File.ReadAllText(xmlFile), overwriteExisting ? TaskCreation.CreateOrUpdate : TaskCreation.Create); + + /// + /// Registers (creates) a new task in the folder using XML to define the task. + /// + /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// An XML-formatted definition of the task. + /// A union of flags. + /// The user credentials used to register the task. + /// The password for the userId used to register the task. + /// A value that defines what logon technique is used to run the registered task. + /// The security descriptor associated with the registered task. You can specify the access control list (ACL) in the security descriptor for a task in order to allow or deny certain users and groups access to a task. + /// A instance that represents the new task. + /// " + + /// "" + + /// " " + + /// " " + + /// " S-1-5-18" + + /// " " + + /// " " + + /// " " + + /// " " + + /// " 2017-09-04T14:04:03" + + /// " " + + /// " " + + /// " " + + /// " " + + /// " " + + /// " cmd" + + /// " " + + /// " " + + /// ""; + /// // Register the task in the root folder of the local machine using the SYSTEM account defined in XML + /// TaskService.Instance.RootFolder.RegisterTaskDefinition("Test", xml); + /// ]]> + public Task RegisterTask(string path, [NotNull] string xmlText, TaskCreation createType = TaskCreation.CreateOrUpdate, string userId = null, string password = null, TaskLogonType logonType = TaskLogonType.S4U, string sddl = null) + { + if (v2Folder != null) + return Task.CreateTask(TaskService, v2Folder.RegisterTask(path, xmlText, (int)createType, userId, password, logonType, sddl)); + + TaskDefinition td = TaskService.NewTask(); + XmlSerializationHelper.ReadObjectFromXmlText(xmlText, td); + return RegisterTaskDefinition(path, td, createType, userId ?? td.Principal.ToString(), + password, logonType == TaskLogonType.S4U ? td.Principal.LogonType : logonType, sddl); + } + + /// + /// Registers (creates) a task in a specified location using a instance to define a task. + /// + /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// The of the registered task. + /// A instance that represents the new task. + /// + /// + public Task RegisterTaskDefinition(string path, [NotNull] TaskDefinition definition) => RegisterTaskDefinition(path, definition, TaskCreation.CreateOrUpdate, + definition.Principal.ToString(), null, definition.Principal.LogonType); + + /// + /// Registers (creates) a task in a specified location using a instance to define a task. + /// + /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// The of the registered task. + /// A union of flags. + /// The user credentials used to register the task. + /// The password for the userId used to register the task. + /// A value that defines what logon technique is used to run the registered task. + /// The security descriptor associated with the registered task. You can specify the access control list (ACL) in the security descriptor for a task in order to allow or deny certain users and groups access to a task. + /// + /// A instance that represents the new task. This will return null if is set to ValidateOnly and there are no validation errors. + /// + /// + /// Task names may not include any characters which are invalid for file names. + /// or + /// Task names ending with a period followed by three or fewer characters cannot be retrieved due to a bug in the native library. + /// + /// This LogonType is not supported on Task Scheduler 1.0. + /// or + /// Security settings are not available on Task Scheduler 1.0. + /// or + /// Registration triggers are not available on Task Scheduler 1.0. + /// or + /// XML validation not available on Task Scheduler 1.0. + /// This method is effectively the "Save" method for tasks. It takes a modified TaskDefinition instance and registers it in the folder defined by this TaskFolder instance. Optionally, you can use this method to override the user, password and logon type defined in the definition and supply security against the task. + /// + /// This first example registers a simple task with a single trigger and action using the default security. + /// + /// This example registers that same task using the SYSTEM account. + /// + /// This example registers that same task using a specific username and password along with a security definition. + /// + public Task RegisterTaskDefinition([NotNull] string path, [NotNull] TaskDefinition definition, TaskCreation createType, string userId, string password = null, TaskLogonType logonType = TaskLogonType.S4U, string sddl = null) + { + if (definition.Actions.Count < 1 || definition.Actions.Count > 32) + throw new ArgumentOutOfRangeException(nameof(definition.Actions), @"A task must be registered with at least one action and no more than 32 actions."); + + userId ??= definition.Principal.Account; + if (userId == string.Empty) userId = null; + User user = new User(userId); + if (v2Folder != null) + { + definition.Actions.ConvertUnsupportedActions(); + if (logonType == TaskLogonType.ServiceAccount) + { + if (string.IsNullOrEmpty(userId) || !user.IsServiceAccount) + throw new ArgumentException(@"A valid system account name must be supplied for TaskLogonType.ServiceAccount. Valid entries are ""NT AUTHORITY\SYSTEM"", ""SYSTEM"", ""NT AUTHORITY\LOCALSERVICE"", or ""NT AUTHORITY\NETWORKSERVICE"".", nameof(userId)); + if (password != null) + throw new ArgumentException(@"A password cannot be supplied when specifying TaskLogonType.ServiceAccount.", nameof(password)); + } + /*else if ((LogonType == TaskLogonType.Password || LogonType == TaskLogonType.InteractiveTokenOrPassword || (LogonType == TaskLogonType.S4U && UserId != null && !user.IsCurrent)) && password == null) { throw new ArgumentException("A password must be supplied when specifying TaskLogonType.Password or TaskLogonType.InteractiveTokenOrPassword or TaskLogonType.S4U from another account.", nameof(password)); }*/ - else if (logonType == TaskLogonType.Group && password != null) - { - throw new ArgumentException(@"A password cannot be supplied when specifying TaskLogonType.Group.", nameof(password)); - } - // The following line compensates for an omission in the native library that never actually sets the registration date (thanks ixm7). - if (definition.RegistrationInfo.Date == DateTime.MinValue) definition.RegistrationInfo.Date = DateTime.Now; - var iRegTask = v2Folder.RegisterTaskDefinition(path, definition.v2Def, (int)createType, userId ?? user.Name, password, logonType, sddl); - if (createType == TaskCreation.ValidateOnly && iRegTask == null) - return null; - return Task.CreateTask(TaskService, iRegTask); - } - - // Check for V1 invalid task names - string invChars = Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())); - if (Regex.IsMatch(path, @"[" + invChars + @"]")) - throw new ArgumentOutOfRangeException(nameof(path), @"Task names may not include any characters which are invalid for file names."); - if (Regex.IsMatch(path, @"\.[^" + invChars + @"]{0,3}\z")) - throw new ArgumentOutOfRangeException(nameof(path), @"Task names ending with a period followed by three or fewer characters cannot be retrieved due to a bug in the native library."); - - // Adds ability to set a password for a V1 task. Provided by Arcao. - TaskFlags flags = definition.v1Task.GetFlags(); - if (logonType == TaskLogonType.InteractiveTokenOrPassword && string.IsNullOrEmpty(password)) - logonType = TaskLogonType.InteractiveToken; - switch (logonType) - { - case TaskLogonType.Group: - case TaskLogonType.S4U: - case TaskLogonType.None: - throw new NotV1SupportedException("This LogonType is not supported on Task Scheduler 1.0."); - case TaskLogonType.InteractiveToken: - flags |= (TaskFlags.RunOnlyIfLoggedOn | TaskFlags.Interactive); - definition.v1Task.SetAccountInformation(user.Name, IntPtr.Zero); - break; - case TaskLogonType.ServiceAccount: - flags &= ~(TaskFlags.Interactive | TaskFlags.RunOnlyIfLoggedOn); - definition.v1Task.SetAccountInformation((String.IsNullOrEmpty(userId) || user.IsSystem) ? String.Empty : user.Name, IntPtr.Zero); - break; - case TaskLogonType.InteractiveTokenOrPassword: - flags |= TaskFlags.Interactive; - using (CoTaskMemString cpwd = new CoTaskMemString(password)) - definition.v1Task.SetAccountInformation(user.Name, cpwd.DangerousGetHandle()); - break; - case TaskLogonType.Password: - using (CoTaskMemString cpwd = new CoTaskMemString(password)) - definition.v1Task.SetAccountInformation(user.Name, cpwd.DangerousGetHandle()); - break; - default: - throw new ArgumentOutOfRangeException(nameof(logonType), logonType, null); - } - definition.v1Task.SetFlags(flags); - - switch (createType) - { - case TaskCreation.Create: - case TaskCreation.CreateOrUpdate: - case TaskCreation.Disable: - case TaskCreation.Update: - if (createType == TaskCreation.Disable) - definition.Settings.Enabled = false; - definition.V1Save(path); - break; - case TaskCreation.DontAddPrincipalAce: - throw new NotV1SupportedException("Security settings are not available on Task Scheduler 1.0."); - case TaskCreation.IgnoreRegistrationTriggers: - throw new NotV1SupportedException("Registration triggers are not available on Task Scheduler 1.0."); - case TaskCreation.ValidateOnly: - throw new NotV1SupportedException("XML validation not available on Task Scheduler 1.0."); - default: - throw new ArgumentOutOfRangeException(nameof(createType), createType, null); - } - return new Task(TaskService, definition.v1Task); - } - - /// - /// Applies access control list (ACL) entries described by a object to the file described by the current object. - /// - /// A object that describes an access control list (ACL) entry to apply to the current folder. - public void SetAccessControl([NotNull] TaskSecurity taskSecurity) { taskSecurity.Persist(this); } - - /// - /// Sets the security descriptor for the folder. Not available to Task Scheduler 1.0. - /// - /// The security descriptor for the folder. - /// Section(s) of the security descriptor to set. - [Obsolete("This method will be removed in deference to the SetAccessControl and SetSecurityDescriptorSddlForm methods.")] - public void SetSecurityDescriptor([NotNull] GenericSecurityDescriptor sd, SecurityInfos includeSections = Task.defaultSecurityInfosSections) { SetSecurityDescriptorSddlForm(sd.GetSddlForm((AccessControlSections)includeSections)); } - - /// - /// Sets the security descriptor for the folder. Not available to Task Scheduler 1.0. - /// - /// The security descriptor for the folder. - /// Flags that specify how to set the security descriptor. - /// Not supported under Task Scheduler 1.0. - public void SetSecurityDescriptorSddlForm([NotNull] string sddlForm, TaskSetSecurityOptions options = TaskSetSecurityOptions.None) - { - if (v2Folder != null) - v2Folder.SetSecurityDescriptor(sddlForm, (int)options); - else - throw new NotV1SupportedException(); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() => Path; - - /// - /// Enumerates the tasks in the specified folder and its child folders. - /// - /// The folder in which to start enumeration. - /// An optional filter to apply to the task list. - /// true if subfolders are to be queried recursively. - /// A that can be used to iterate through the tasks. - internal static IEnumerable EnumerateFolderTasks(TaskFolder folder, Predicate filter = null, bool recurse = true) - { - foreach (var task in folder.Tasks) - if (filter == null || filter(task)) - yield return task; - - if (!recurse) yield break; - - foreach (var sfld in folder.SubFolders) - foreach (var task in EnumerateFolderTasks(sfld, filter)) - yield return task; - } - } + else if (logonType == TaskLogonType.Group && password != null) + { + throw new ArgumentException(@"A password cannot be supplied when specifying TaskLogonType.Group.", nameof(password)); + } + // The following line compensates for an omission in the native library that never actually sets the registration date (thanks ixm7). + if (definition.RegistrationInfo.Date == DateTime.MinValue) definition.RegistrationInfo.Date = DateTime.Now; + var iRegTask = v2Folder.RegisterTaskDefinition(path, definition.v2Def, (int)createType, userId ?? user.Name, password, logonType, sddl); + if (createType == TaskCreation.ValidateOnly && iRegTask == null) + return null; + return Task.CreateTask(TaskService, iRegTask); + } + + // Check for V1 invalid task names + string invChars = Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())); + if (Regex.IsMatch(path, @"[" + invChars + @"]")) + throw new ArgumentOutOfRangeException(nameof(path), @"Task names may not include any characters which are invalid for file names."); + if (Regex.IsMatch(path, @"\.[^" + invChars + @"]{0,3}\z")) + throw new ArgumentOutOfRangeException(nameof(path), @"Task names ending with a period followed by three or fewer characters cannot be retrieved due to a bug in the native library."); + + // Adds ability to set a password for a V1 task. Provided by Arcao. + TaskFlags flags = definition.v1Task.GetFlags(); + if (logonType == TaskLogonType.InteractiveTokenOrPassword && string.IsNullOrEmpty(password)) + logonType = TaskLogonType.InteractiveToken; + switch (logonType) + { + case TaskLogonType.Group: + case TaskLogonType.S4U: + case TaskLogonType.None: + throw new NotV1SupportedException("This LogonType is not supported on Task Scheduler 1.0."); + case TaskLogonType.InteractiveToken: + flags |= (TaskFlags.RunOnlyIfLoggedOn | TaskFlags.Interactive); + definition.v1Task.SetAccountInformation(user.Name, IntPtr.Zero); + break; + case TaskLogonType.ServiceAccount: + flags &= ~(TaskFlags.Interactive | TaskFlags.RunOnlyIfLoggedOn); + definition.v1Task.SetAccountInformation((String.IsNullOrEmpty(userId) || user.IsSystem) ? String.Empty : user.Name, IntPtr.Zero); + break; + case TaskLogonType.InteractiveTokenOrPassword: + flags |= TaskFlags.Interactive; + using (CoTaskMemString cpwd = new CoTaskMemString(password)) + definition.v1Task.SetAccountInformation(user.Name, cpwd.DangerousGetHandle()); + break; + case TaskLogonType.Password: + using (CoTaskMemString cpwd = new CoTaskMemString(password)) + definition.v1Task.SetAccountInformation(user.Name, cpwd.DangerousGetHandle()); + break; + default: + throw new ArgumentOutOfRangeException(nameof(logonType), logonType, null); + } + definition.v1Task.SetFlags(flags); + + switch (createType) + { + case TaskCreation.Create: + case TaskCreation.CreateOrUpdate: + case TaskCreation.Disable: + case TaskCreation.Update: + if (createType == TaskCreation.Disable) + definition.Settings.Enabled = false; + definition.V1Save(path); + break; + case TaskCreation.DontAddPrincipalAce: + throw new NotV1SupportedException("Security settings are not available on Task Scheduler 1.0."); + case TaskCreation.IgnoreRegistrationTriggers: + throw new NotV1SupportedException("Registration triggers are not available on Task Scheduler 1.0."); + case TaskCreation.ValidateOnly: + throw new NotV1SupportedException("XML validation not available on Task Scheduler 1.0."); + default: + throw new ArgumentOutOfRangeException(nameof(createType), createType, null); + } + return new Task(TaskService, definition.v1Task); + } + + /// + /// Applies access control list (ACL) entries described by a object to the file described by the current object. + /// + /// A object that describes an access control list (ACL) entry to apply to the current folder. + public void SetAccessControl([NotNull] TaskSecurity taskSecurity) { taskSecurity.Persist(this); } + + /// + /// Sets the security descriptor for the folder. Not available to Task Scheduler 1.0. + /// + /// The security descriptor for the folder. + /// Section(s) of the security descriptor to set. + [Obsolete("This method will be removed in deference to the SetAccessControl and SetSecurityDescriptorSddlForm methods.")] + public void SetSecurityDescriptor([NotNull] GenericSecurityDescriptor sd, SecurityInfos includeSections = Task.defaultSecurityInfosSections) { SetSecurityDescriptorSddlForm(sd.GetSddlForm((AccessControlSections)includeSections)); } + + /// + /// Sets the security descriptor for the folder. Not available to Task Scheduler 1.0. + /// + /// The security descriptor for the folder. + /// Flags that specify how to set the security descriptor. + /// Not supported under Task Scheduler 1.0. + public void SetSecurityDescriptorSddlForm([NotNull] string sddlForm, TaskSetSecurityOptions options = TaskSetSecurityOptions.None) + { + if (v2Folder != null) + v2Folder.SetSecurityDescriptor(sddlForm, (int)options); + else + throw new NotV1SupportedException(); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() => Path; + + /// + /// Enumerates the tasks in the specified folder and its child folders. + /// + /// The folder in which to start enumeration. + /// An optional filter to apply to the task list. + /// true if subfolders are to be queried recursively. + /// A that can be used to iterate through the tasks. + internal static IEnumerable EnumerateFolderTasks(TaskFolder folder, Predicate filter = null, bool recurse = true) + { + foreach (var task in folder.Tasks) + if (filter == null || filter(task)) + yield return task; + + if (!recurse) yield break; + + foreach (var sfld in folder.SubFolders) + foreach (var task in EnumerateFolderTasks(sfld, filter)) + yield return task; + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolderCollection.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolderCollection.cs index 469aa691d..2ad46f1bb 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolderCollection.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskFolderCollection.cs @@ -2,173 +2,170 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; using winPEAS.TaskScheduler.TaskEditor.Native; using winPEAS.TaskScheduler.V2; namespace winPEAS.TaskScheduler { - /// Provides information and control for a collection of folders that contain tasks. - public sealed class TaskFolderCollection : ICollection, IDisposable, INotifyCollectionChanged, INotifyPropertyChanged - { - private const string IndexerName = "Item[]"; - private readonly TaskFolder parent; - private readonly TaskFolder[] v1FolderList; - private readonly ITaskFolderCollection v2FolderList; - - internal TaskFolderCollection() => v1FolderList = new TaskFolder[0]; - - internal TaskFolderCollection([NotNull] TaskFolder folder, [NotNull] ITaskFolderCollection iCollection) - { - parent = folder; - v2FolderList = iCollection; - } - - /// Occurs when a collection changes. - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets the number of items in the collection. - public int Count => v2FolderList?.Count ?? v1FolderList.Length; - - /// Gets a value indicating whether the is read-only. - bool ICollection.IsReadOnly => false; - - /// Gets the specified folder from the collection. - /// The index of the folder to be retrieved. - /// A TaskFolder instance that represents the requested folder. - public TaskFolder this[int index] - { - get - { - if (v2FolderList != null) - return new TaskFolder(parent.TaskService, v2FolderList[++index]); - return v1FolderList[index]; - } - } - - /// Gets the specified folder from the collection. - /// The path of the folder to be retrieved. - /// A TaskFolder instance that represents the requested folder. - public TaskFolder this[[NotNull] string path] - { - get - { - try - { - if (v2FolderList != null) - return parent.GetFolder(path); - if (v1FolderList != null && v1FolderList.Length > 0 && (path == string.Empty || path == "\\")) - return v1FolderList[0]; - } - catch { } - throw new ArgumentException(@"Path not found", nameof(path)); - } - } - - /// Adds an item to the . - /// The object to add to the . - /// - /// This action is technically unfeasible due to limitations of the underlying library. Use the instead. - /// - public void Add([NotNull] TaskFolder item) => throw new NotImplementedException(); - - /// Removes all items from the . - public void Clear() - { - if (v2FolderList != null) - { - for (var i = v2FolderList.Count; i > 0; i--) - parent.DeleteFolder(v2FolderList[i].Name, false); - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - } - - /// Determines whether the contains a specific value. - /// The object to locate in the . - /// true if is found in the ; otherwise, false. - public bool Contains([NotNull] TaskFolder item) - { - if (v2FolderList != null) - { - for (var i = v2FolderList.Count; i > 0; i--) - if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase)) - return true; - } - else - return item.Path == "\\"; - return false; - } - - /// Copies the elements of the ICollection to an Array, starting at a particular Array index. - /// - /// The one-dimensional Array that is the destination of the elements copied from . The Array must have - /// zero-based indexing. - /// - /// The zero-based index in array at which copying begins. - public void CopyTo(TaskFolder[] array, int arrayIndex) - { - if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - if (array == null) throw new ArgumentNullException(nameof(array)); - if (v2FolderList != null) - { - if (arrayIndex + Count > array.Length) - throw new ArgumentException(); - foreach (var f in this) - array[arrayIndex++] = f; - } - else - { - if (arrayIndex + v1FolderList.Length > array.Length) - throw new ArgumentException(); - v1FolderList.CopyTo(array, arrayIndex); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v1FolderList != null && v1FolderList.Length > 0) - { - v1FolderList[0].Dispose(); - v1FolderList[0] = null; - } - if (v2FolderList != null) - System.Runtime.InteropServices.Marshal.ReleaseComObject(v2FolderList); - } - - /// Determines whether the specified folder exists. - /// The path of the folder. - /// true if folder exists; otherwise, false. - public bool Exists([NotNull] string path) - { - try - { - parent.GetFolder(path); - return true; - } - catch { } - return false; - } - - /// Gets a list of items in a collection. - /// Enumerated list of items in the collection. - public IEnumerator GetEnumerator() - { - if (v2FolderList != null) - return new ComEnumerator(() => v2FolderList.Count, (object o) => v2FolderList[o], o => new TaskFolder(parent.TaskService, o)); - return Array.AsReadOnly(v1FolderList).GetEnumerator(); - } - - /* + /// Provides information and control for a collection of folders that contain tasks. + public sealed class TaskFolderCollection : ICollection, IDisposable, INotifyCollectionChanged, INotifyPropertyChanged + { + private const string IndexerName = "Item[]"; + private readonly TaskFolder parent; + private readonly TaskFolder[] v1FolderList; + private readonly ITaskFolderCollection v2FolderList; + + internal TaskFolderCollection() => v1FolderList = new TaskFolder[0]; + + internal TaskFolderCollection([NotNull] TaskFolder folder, [NotNull] ITaskFolderCollection iCollection) + { + parent = folder; + v2FolderList = iCollection; + } + + /// Occurs when a collection changes. + public event NotifyCollectionChangedEventHandler CollectionChanged; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets the number of items in the collection. + public int Count => v2FolderList?.Count ?? v1FolderList.Length; + + /// Gets a value indicating whether the is read-only. + bool ICollection.IsReadOnly => false; + + /// Gets the specified folder from the collection. + /// The index of the folder to be retrieved. + /// A TaskFolder instance that represents the requested folder. + public TaskFolder this[int index] + { + get + { + if (v2FolderList != null) + return new TaskFolder(parent.TaskService, v2FolderList[++index]); + return v1FolderList[index]; + } + } + + /// Gets the specified folder from the collection. + /// The path of the folder to be retrieved. + /// A TaskFolder instance that represents the requested folder. + public TaskFolder this[[NotNull] string path] + { + get + { + try + { + if (v2FolderList != null) + return parent.GetFolder(path); + if (v1FolderList != null && v1FolderList.Length > 0 && (path == string.Empty || path == "\\")) + return v1FolderList[0]; + } + catch { } + throw new ArgumentException(@"Path not found", nameof(path)); + } + } + + /// Adds an item to the . + /// The object to add to the . + /// + /// This action is technically unfeasible due to limitations of the underlying library. Use the instead. + /// + public void Add([NotNull] TaskFolder item) => throw new NotImplementedException(); + + /// Removes all items from the . + public void Clear() + { + if (v2FolderList != null) + { + for (var i = v2FolderList.Count; i > 0; i--) + parent.DeleteFolder(v2FolderList[i].Name, false); + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + + /// Determines whether the contains a specific value. + /// The object to locate in the . + /// true if is found in the ; otherwise, false. + public bool Contains([NotNull] TaskFolder item) + { + if (v2FolderList != null) + { + for (var i = v2FolderList.Count; i > 0; i--) + if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase)) + return true; + } + else + return item.Path == "\\"; + return false; + } + + /// Copies the elements of the ICollection to an Array, starting at a particular Array index. + /// + /// The one-dimensional Array that is the destination of the elements copied from . The Array must have + /// zero-based indexing. + /// + /// The zero-based index in array at which copying begins. + public void CopyTo(TaskFolder[] array, int arrayIndex) + { + if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + if (array == null) throw new ArgumentNullException(nameof(array)); + if (v2FolderList != null) + { + if (arrayIndex + Count > array.Length) + throw new ArgumentException(); + foreach (var f in this) + array[arrayIndex++] = f; + } + else + { + if (arrayIndex + v1FolderList.Length > array.Length) + throw new ArgumentException(); + v1FolderList.CopyTo(array, arrayIndex); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v1FolderList != null && v1FolderList.Length > 0) + { + v1FolderList[0].Dispose(); + v1FolderList[0] = null; + } + if (v2FolderList != null) + System.Runtime.InteropServices.Marshal.ReleaseComObject(v2FolderList); + } + + /// Determines whether the specified folder exists. + /// The path of the folder. + /// true if folder exists; otherwise, false. + public bool Exists([NotNull] string path) + { + try + { + parent.GetFolder(path); + return true; + } + catch { } + return false; + } + + /// Gets a list of items in a collection. + /// Enumerated list of items in the collection. + public IEnumerator GetEnumerator() + { + if (v2FolderList != null) + return new ComEnumerator(() => v2FolderList.Count, (object o) => v2FolderList[o], o => new TaskFolder(parent.TaskService, o)); + return Array.AsReadOnly(v1FolderList).GetEnumerator(); + } + + /* /// Returns the index of the TaskFolder within the collection. /// TaskFolder to find. /// Index of the TaskFolder; -1 if not found. @@ -196,42 +193,42 @@ public int IndexOf(string path) } */ - /// Removes the first occurrence of a specific object from the . - /// The object to remove from the . - /// - /// true if was successfully removed from the ; otherwise, false. This method - /// also returns false if is not found in the original . - /// - public bool Remove([NotNull] TaskFolder item) - { - if (v2FolderList != null) - { - for (var i = v2FolderList.Count; i > 0; i--) - { - if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase)) - { - try - { - parent.DeleteFolder(v2FolderList[i].Name); - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, i)); - } - catch - { - return false; - } - return true; - } - } - } - return false; - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + /// Removes the first occurrence of a specific object from the . + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method + /// also returns false if is not found in the original . + /// + public bool Remove([NotNull] TaskFolder item) + { + if (v2FolderList != null) + { + for (var i = v2FolderList.Count; i > 0; i--) + { + if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase)) + { + try + { + parent.DeleteFolder(v2FolderList[i].Name); + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, i)); + } + catch + { + return false; + } + return true; + } + } + } + return false; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskHandlerInterfaces.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskHandlerInterfaces.cs index b25b562b6..ed96544f4 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskHandlerInterfaces.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskHandlerInterfaces.cs @@ -1,58 +1,54 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace winPEAS.TaskScheduler { - /// - /// Defines the methods that are called by the Task Scheduler service to manage a COM handler. - /// - /// - /// This interface must be implemented for a task to perform a COM handler action. When the Task Scheduler performs a COM handler action, it creates and activates the handler and calls the methods of this interface as needed. For information on specifying a COM handler action, see the class. - /// - [ComImport, Guid("839D7762-5121-4009-9234-4F0D19394F04"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] - public interface ITaskHandler - { - /// - /// Called to start the COM handler. This method must be implemented by the handler. - /// - /// An IUnkown interface that is used to communicate back with the Task Scheduler. - /// The arguments that are required by the handler. These arguments are defined in the property of the COM handler action. - void Start([In, MarshalAs(UnmanagedType.IUnknown)] object pHandlerServices, [In, MarshalAs(UnmanagedType.BStr)] string data); - /// - /// Called to stop the COM handler. This method must be implemented by the handler. - /// - /// The return code that the Task Schedule will raise as an event when the COM handler action is completed. - void Stop([MarshalAs(UnmanagedType.Error)] out int pRetCode); - /// - /// Called to pause the COM handler. This method is optional and should only be implemented to give the Task Scheduler the ability to pause and restart the handler. - /// - void Pause(); - /// - /// Called to resume the COM handler. This method is optional and should only be implemented to give the Task Scheduler the ability to resume the handler. - /// - void Resume(); - } + /// + /// Defines the methods that are called by the Task Scheduler service to manage a COM handler. + /// + /// + /// This interface must be implemented for a task to perform a COM handler action. When the Task Scheduler performs a COM handler action, it creates and activates the handler and calls the methods of this interface as needed. For information on specifying a COM handler action, see the class. + /// + [ComImport, Guid("839D7762-5121-4009-9234-4F0D19394F04"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] + public interface ITaskHandler + { + /// + /// Called to start the COM handler. This method must be implemented by the handler. + /// + /// An IUnkown interface that is used to communicate back with the Task Scheduler. + /// The arguments that are required by the handler. These arguments are defined in the property of the COM handler action. + void Start([In, MarshalAs(UnmanagedType.IUnknown)] object pHandlerServices, [In, MarshalAs(UnmanagedType.BStr)] string data); + /// + /// Called to stop the COM handler. This method must be implemented by the handler. + /// + /// The return code that the Task Schedule will raise as an event when the COM handler action is completed. + void Stop([MarshalAs(UnmanagedType.Error)] out int pRetCode); + /// + /// Called to pause the COM handler. This method is optional and should only be implemented to give the Task Scheduler the ability to pause and restart the handler. + /// + void Pause(); + /// + /// Called to resume the COM handler. This method is optional and should only be implemented to give the Task Scheduler the ability to resume the handler. + /// + void Resume(); + } - /// - /// Provides the methods that are used by COM handlers to notify the Task Scheduler about the status of the handler. - /// - [ComImport, Guid("EAEC7A8F-27A0-4DDC-8675-14726A01A38A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] - public interface ITaskHandlerStatus - { - /// - /// Tells the Task Scheduler about the percentage of completion of the COM handler. - /// - /// A value that indicates the percentage of completion for the COM handler. - /// The message that is displayed in the Task Scheduler UI. - void UpdateStatus([In] short percentComplete, [In, MarshalAs(UnmanagedType.BStr)] string statusMessage); - /// - /// Tells the Task Scheduler that the COM handler is completed. - /// - /// The error code that the Task Scheduler will raise as an event. - void TaskCompleted([In, MarshalAs(UnmanagedType.Error)] int taskErrCode); - } + /// + /// Provides the methods that are used by COM handlers to notify the Task Scheduler about the status of the handler. + /// + [ComImport, Guid("EAEC7A8F-27A0-4DDC-8675-14726A01A38A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] + public interface ITaskHandlerStatus + { + /// + /// Tells the Task Scheduler about the percentage of completion of the COM handler. + /// + /// A value that indicates the percentage of completion for the COM handler. + /// The message that is displayed in the Task Scheduler UI. + void UpdateStatus([In] short percentComplete, [In, MarshalAs(UnmanagedType.BStr)] string statusMessage); + /// + /// Tells the Task Scheduler that the COM handler is completed. + /// + /// The error code that the Task Scheduler will raise as an event. + void TaskCompleted([In, MarshalAs(UnmanagedType.Error)] int taskErrCode); + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskSecurity.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskSecurity.cs index 9da16241e..45595c106 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskSecurity.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskSecurity.cs @@ -9,456 +9,456 @@ namespace winPEAS.TaskScheduler /// Specifies the access control rights that can be applied to Task Scheduler tasks. /// [Flags] - public enum TaskRights - { - /// Specifies the right to exert full control over a task folder or task, and to modify access control and audit rules. This value represents the right to do anything with a task and is the combination of all rights in this enumeration. - FullControl = 0x1f01ff, - /// Specifies the right to create tasks and folders, and to add or remove data from tasks. This right includes the following rights: . - Write = 0x120116, - /// Specifies the right to open and copy folders or tasks as read-only. This right includes the following rights: . - Read = 0x120089, - /// Specifies the right run tasks. This right includes the following rights: . - Execute = 0x120089, - /// The right to wait on a task. - Synchronize = 0x100000, - /// The right to change the owner of a task. - TakeOwnership = 0x80000, - /// Specifies the right to change the security and audit rules associated with a task or folder. - ChangePermissions = 0x40000, - /// The right to open and copy the access rules and audit rules for a task. - ReadPermissions = 0x20000, - /// The right to delete a folder or task. - Delete = 0x10000, - /// Specifies the right to open and write file system attributes to a folder or file. This does not include the ability to write data, extended attributes, or access and audit rules. - WriteAttributes = 0x100, - /// Specifies the right to open and copy file system attributes from a folder or task. For example, this value specifies the right to view the file creation or modified date. This does not include the right to read data, extended file system attributes, or access and audit rules. - ReadAttributes = 0x80, - /// Specifies the right to delete a folder and any tasks contained within that folder. - DeleteChild = 0x40, - /// Specifies the right to run a task. - ExecuteFile = 0x20, - /// Specifies the right to open and write extended file system attributes to a folder or file. This does not include the ability to write data, attributes, or access and audit rules. - WriteExtendedAttributes = 0x10, - /// Specifies the right to open and copy extended system attributes from a folder or task. For example, this value specifies the right to view author and content information. This does not include the right to read data, system attributes, or access and audit rules. - ReadExtendedAttributes = 8, - /// Specifies the right to append data to the end of a file. - AppendData = 4, - /// Specifies the right to open and write to a file or folder. This does not include the right to open and write file system attributes, extended file system attributes, or access and audit rules. - WriteData = 2, - /// Specifies the right to open and copy a task or folder. This does not include the right to read file system attributes, extended file system attributes, or access and audit rules. - ReadData = 1, - } - - /// - /// Represents a set of access rights allowed or denied for a user or group. This class cannot be inherited. - /// - public sealed class TaskAccessRule : AccessRule - { - /// - /// Initializes a new instance of the class, specifying the user or group the rule applies to, the access rights, and whether the specified access rights are allowed or denied. - /// - /// The user or group the rule applies to. Must be of type or a type such as that can be converted to type . - /// A bitwise combination of values specifying the rights allowed or denied. - /// One of the values specifying whether the rights are allowed or denied. - public TaskAccessRule([NotNull] IdentityReference identity, TaskRights eventRights, AccessControlType type) - : this(identity, (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, type) - { - } - - /// - /// Initializes a new instance of the class, specifying the name of the user or group the rule applies to, the access rights, and whether the specified access rights are allowed or denied. - /// - /// The name of the user or group the rule applies to. - /// A bitwise combination of values specifying the rights allowed or denied. - /// One of the values specifying whether the rights are allowed or denied. - public TaskAccessRule([NotNull] string identity, TaskRights eventRights, AccessControlType type) - : this(new NTAccount(identity), (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, type) - { - } - - private TaskAccessRule([NotNull] IdentityReference identity, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) - : base(identity, accessMask, isInherited, inheritanceFlags, propagationFlags, type) - { - } - - /// - /// Gets the rights allowed or denied by the access rule. - /// - /// - /// A bitwise combination of values indicating the rights allowed or denied by the access rule. - /// - public TaskRights TaskRights => (TaskRights)AccessMask; - } - - /// - /// Represents a set of access rights to be audited for a user or group. This class cannot be inherited. - /// - public sealed class TaskAuditRule : AuditRule - { - /// - /// Initializes a new instance of the class, specifying the user or group to audit, the rights to audit, and whether to audit success, failure, or both. - /// - /// The user or group the rule applies to. Must be of type or a type such as that can be converted to type . - /// A bitwise combination of values specifying the kinds of access to audit. - /// The audit flags. - public TaskAuditRule([NotNull] IdentityReference identity, TaskRights eventRights, AuditFlags flags) - : this(identity, (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, flags) - { - } - - internal TaskAuditRule([NotNull] IdentityReference identity, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) - : base(identity, accessMask, isInherited, inheritanceFlags, propagationFlags, flags) - { - } - - /// - /// Gets the access rights affected by the audit rule. - /// - /// - /// A bitwise combination of values that indicates the rights affected by the audit rule. - /// - /// objects are immutable. You can create a new audit rule representing a different user, different rights, or a different combination of AuditFlags values, but you cannot modify an existing audit rule. - public TaskRights TaskRights => (TaskRights)AccessMask; - } - - /// - /// Represents the Windows access control security for a Task Scheduler task. This class cannot be inherited. - /// - /// - /// A TaskSecurity object specifies access rights for a Task Scheduler task, and also specifies how access attempts are audited. Access rights to the task are expressed as rules, with each access rule represented by a object. Each auditing rule is represented by a object. - /// This mirrors the underlying Windows security system, in which each securable object has at most one discretionary access control list (DACL) that controls access to the secured object, and at most one system access control list (SACL) that specifies which access attempts are audited. The DACL and SACL are ordered lists of access control entries (ACE) that specify access and auditing for users and groups. A or object might represent more than one ACE. - /// Note - /// A object can represent a local task or a Task Scheduler task. Windows access control security is meaningful only for Task Scheduler tasks. - /// The TaskSecurity, , and classes hide the implementation details of ACLs and ACEs. They allow you to ignore the seventeen different ACE types and the complexity of correctly maintaining inheritance and propagation of access rights. These objects are also designed to prevent the following common access control errors: - /// - /// Creating a security descriptor with a null DACL. A null reference to a DACL allows any user to add access rules to an object, potentially creating a denial-of-service attack. A new TaskSecurity object always starts with an empty DACL, which denies all access for all users. - /// Violating the canonical ordering of ACEs. If the ACE list in the DACL is not kept in the canonical order, users might inadvertently be given access to the secured object. For example, denied access rights must always appear before allowed access rights. TaskSecurity objects maintain the correct order internally. - /// Manipulating security descriptor flags, which should be under resource manager control only. - /// Creating invalid combinations of ACE flags. - /// Manipulating inherited ACEs. Inheritance and propagation are handled by the resource manager, in response to changes you make to access and audit rules. - /// Inserting meaningless ACEs into ACLs. - /// - /// The only capabilities not supported by the .NET security objects are dangerous activities that should be avoided by the majority of application developers, such as the following: - /// - /// Low-level tasks that are normally performed by the resource manager. - /// Adding or removing access control entries in ways that do not maintain the canonical ordering. - /// - /// To modify Windows access control security for a task, use the method to get the TaskSecurity object. Modify the security object by adding and removing rules, and then use the method to reattach it. - /// Important: Changes you make to a TaskSecurity object do not affect the access levels of the task until you call the method to assign the altered security object to the task. - /// To copy access control security from one task to another, use the method to get a TaskSecurity object representing the access and audit rules for the first task, then use the method, or a constructor that accepts a TaskSecurity object, to assign those rules to the second task. - /// Users with an investment in the security descriptor definition language (SDDL) can use the method to set access rules for a task, and the method to obtain a string that represents the access rules in SDDL format. This is not recommended for new development. - /// - public sealed class TaskSecurity : CommonObjectSecurity - { - /// - /// Initializes a new instance of the class with default values. - /// - public TaskSecurity() - : base(false) - { - } - - /// - /// Initializes a new instance of the class with the specified sections of the access control security rules from the specified task. - /// - /// The task. - /// The sections of the ACL to retrieve. - public TaskSecurity([NotNull] Task task, AccessControlSections sections = Task.defaultAccessControlSections) - : base(false) - { - SetSecurityDescriptorSddlForm(task.GetSecurityDescriptorSddlForm(Convert(sections)), sections); - this.CanonicalizeAccessRules(); - } - - /// - /// Initializes a new instance of the class with the specified sections of the access control security rules from the specified task. - /// - /// The folder. - /// The sections of the ACL to retrieve. - public TaskSecurity([NotNull] TaskFolder folder, AccessControlSections sections = Task.defaultAccessControlSections) - : base(false) - { - SetSecurityDescriptorSddlForm(folder.GetSecurityDescriptorSddlForm(Convert(sections)), sections); - this.CanonicalizeAccessRules(); - } - - /// - /// Gets the enumeration that the class uses to represent access rights. - /// - /// A object representing the enumeration. - public override Type AccessRightType => typeof(TaskRights); - - /// - /// Gets the type that the TaskSecurity class uses to represent access rules. - /// - /// A object representing the class. - public override Type AccessRuleType => typeof(TaskAccessRule); - - /// - /// Gets the type that the TaskSecurity class uses to represent audit rules. - /// - /// A object representing the class. - public override Type AuditRuleType => typeof(TaskAuditRule); - - /// - /// Gets a object that represent the default access rights. - /// - /// The default task security. - public static TaskSecurity DefaultTaskSecurity - { - get - { - var ret = new TaskSecurity(); - ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), TaskRights.FullControl, AccessControlType.Allow)); - ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null), TaskRights.Read | TaskRights.Write | TaskRights.Execute, AccessControlType.Allow)); - ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null), TaskRights.Read, AccessControlType.Allow)); - ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), TaskRights.Read, AccessControlType.Allow)); - ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null), TaskRights.Read, AccessControlType.Allow)); - return ret; - } - } - - /// - /// Creates a new access control rule for the specified user, with the specified access rights, access control, and flags. - /// - /// An that identifies the user or group the rule applies to. - /// A bitwise combination of values specifying the access rights to allow or deny, cast to an integer. - /// Meaningless for tasks, because they have no hierarchy. - /// Meaningless for tasks, because they have no hierarchy. - /// Meaningless for tasks, because they have no hierarchy. - /// One of the values specifying whether the rights are allowed or denied. - /// - /// The object that this method creates. - /// - public override AccessRule AccessRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) => new TaskAccessRule(identityReference, (TaskRights)accessMask, type); - - /// - /// Searches for a matching rule with which the new rule can be merged. If none are found, adds the new rule. - /// - /// The access control rule to add. - public void AddAccessRule([NotNull] TaskAccessRule rule) - { - base.AddAccessRule(rule); - } - - /// - /// Searches for an audit rule with which the new rule can be merged. If none are found, adds the new rule. - /// - /// The audit rule to add. The user specified by this rule determines the search. - public void AddAuditRule([NotNull] TaskAuditRule rule) - { - base.AddAuditRule(rule); - } - - /// - /// Creates a new audit rule, specifying the user the rule applies to, the access rights to audit, and the outcome that triggers the audit rule. - /// - /// An that identifies the user or group the rule applies to. - /// A bitwise combination of values specifying the access rights to audit, cast to an integer. - /// Meaningless for tasks, because they have no hierarchy. - /// Meaningless for tasks, because they have no hierarchy. - /// Meaningless for tasks, because they have no hierarchy. - /// One of the values specifying whether to audit successful access, failed access, or both. - /// - /// A object representing the specified audit rule for the specified user. The return type of the method is the base class, , but the return value can be cast safely to the derived class. - /// - public override AuditRule AuditRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) => new TaskAuditRule(identityReference, accessMask, isInherited, inheritanceFlags, propagationFlags, flags); - - /// - /// Searches for an access control rule with the same user and (allow or deny) as the specified rule, and with compatible inheritance and propagation flags; if such a rule is found, the rights contained in the specified access rule are removed from it. - /// - /// A that specifies the user and to search for, and a set of inheritance and propagation flags that a matching rule, if found, must be compatible with. Specifies the rights to remove from the compatible rule, if found. - /// true if a compatible rule is found; otherwise false. - public bool RemoveAccessRule([NotNull] TaskAccessRule rule) => base.RemoveAccessRule(rule); - - /// - /// Searches for all access control rules with the same user and (allow or deny) as the specified rule and, if found, removes them. - /// - /// A that specifies the user and to search for, and a set of inheritance and propagation flags that a matching rule, if found, must be compatible with. Any rights specified by this rule are ignored. - public void RemoveAccessRuleAll([NotNull] TaskAccessRule rule) - { - base.RemoveAccessRuleAll(rule); - } - - /// - /// Searches for an access control rule that exactly matches the specified rule and, if found, removes it. - /// - /// The to remove. - public void RemoveAccessRuleSpecific([NotNull] TaskAccessRule rule) - { - base.RemoveAccessRuleSpecific(rule); - } - - /// - /// Searches for an audit control rule with the same user as the specified rule, and with compatible inheritance and propagation flags; if a compatible rule is found, the rights contained in the specified rule are removed from it. - /// - /// A that specifies the user to search for, and a set of inheritance and propagation flags that a matching rule, if found, must be compatible with. Specifies the rights to remove from the compatible rule, if found. - /// true if a compatible rule is found; otherwise false. - public bool RemoveAuditRule([NotNull] TaskAuditRule rule) => base.RemoveAuditRule(rule); - - /// - /// Searches for all audit rules with the same user as the specified rule and, if found, removes them. - /// - /// A that specifies the user to search for. Any rights specified by this rule are ignored. - public void RemoveAuditRuleAll(TaskAuditRule rule) - { - base.RemoveAuditRuleAll(rule); - } - - /// - /// Searches for an audit rule that exactly matches the specified rule and, if found, removes it. - /// - /// The to remove. - public void RemoveAuditRuleSpecific([NotNull] TaskAuditRule rule) - { - base.RemoveAuditRuleSpecific(rule); - } - - /// - /// Removes all access control rules with the same user as the specified rule, regardless of , and then adds the specified rule. - /// - /// The to add. The user specified by this rule determines the rules to remove before this rule is added. - public void ResetAccessRule([NotNull] TaskAccessRule rule) - { - base.ResetAccessRule(rule); - } - - /// - /// Removes all access control rules with the same user and (allow or deny) as the specified rule, and then adds the specified rule. - /// - /// The to add. The user and of this rule determine the rules to remove before this rule is added. - public void SetAccessRule([NotNull] TaskAccessRule rule) - { - base.SetAccessRule(rule); - } - - /// - /// Removes all audit rules with the same user as the specified rule, regardless of the value, and then adds the specified rule. - /// - /// The to add. The user specified by this rule determines the rules to remove before this rule is added. - public void SetAuditRule([NotNull] TaskAuditRule rule) - { - base.SetAuditRule(rule); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() => GetSecurityDescriptorSddlForm(Task.defaultAccessControlSections); - - private static SecurityInfos Convert(AccessControlSections si) - { - SecurityInfos ret = 0; - if ((si & AccessControlSections.Audit) != 0) - ret |= SecurityInfos.SystemAcl; - if ((si & AccessControlSections.Access) != 0) - ret |= SecurityInfos.DiscretionaryAcl; - if ((si & AccessControlSections.Group) != 0) - ret |= SecurityInfos.Group; - if ((si & AccessControlSections.Owner) != 0) - ret |= SecurityInfos.Owner; - return ret; - } - - private static AccessControlSections Convert(SecurityInfos si) - { - AccessControlSections ret = AccessControlSections.None; - if ((si & SecurityInfos.SystemAcl) != 0) - ret |= AccessControlSections.Audit; - if ((si & SecurityInfos.DiscretionaryAcl) != 0) - ret |= AccessControlSections.Access; - if ((si & SecurityInfos.Group) != 0) - ret |= AccessControlSections.Group; - if ((si & SecurityInfos.Owner) != 0) - ret |= AccessControlSections.Owner; - return ret; - } - - private AccessControlSections GetAccessControlSectionsFromChanges() - { - AccessControlSections none = AccessControlSections.None; - if (AccessRulesModified) - { - none = AccessControlSections.Access; - } - if (AuditRulesModified) - { - none |= AccessControlSections.Audit; - } - if (OwnerModified) - { - none |= AccessControlSections.Owner; - } - if (GroupModified) - { - none |= AccessControlSections.Group; - } - return none; - } - - /// - /// Saves the specified sections of the security descriptor associated with this object to permanent storage. We recommend that the values of the parameters passed to the constructor and persist methods be identical. - /// - /// The task used to retrieve the persisted information. - /// One of the enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save. - [SecurityCritical] - internal void Persist([NotNull] Task task, AccessControlSections includeSections = Task.defaultAccessControlSections) - { - WriteLock(); - try - { - AccessControlSections accessControlSectionsFromChanges = GetAccessControlSectionsFromChanges(); - if (accessControlSectionsFromChanges != AccessControlSections.None) - { - task.SetSecurityDescriptorSddlForm(GetSecurityDescriptorSddlForm(accessControlSectionsFromChanges)); - OwnerModified = GroupModified = AccessRulesModified = AuditRulesModified = false; - } - } - finally - { - WriteUnlock(); - } - } - - /// - /// Saves the specified sections of the security descriptor associated with this object to permanent storage. We recommend that the values of the parameters passed to the constructor and persist methods be identical. - /// - /// The task folder used to retrieve the persisted information. - /// One of the enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save. - [SecurityCritical] - internal void Persist([NotNull] TaskFolder folder, AccessControlSections includeSections = Task.defaultAccessControlSections) - { - WriteLock(); - try - { - AccessControlSections accessControlSectionsFromChanges = GetAccessControlSectionsFromChanges(); - if (accessControlSectionsFromChanges != AccessControlSections.None) - { - folder.SetSecurityDescriptorSddlForm(GetSecurityDescriptorSddlForm(accessControlSectionsFromChanges)); - OwnerModified = GroupModified = AccessRulesModified = AuditRulesModified = false; - } - } - finally - { - WriteUnlock(); - } - } - - /// - /// Saves the specified sections of the security descriptor associated with this object to permanent storage. We recommend that the values of the parameters passed to the constructor and persist methods be identical. For more information, see Remarks. - /// - /// The name used to retrieve the persisted information. - /// One of the enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save. - protected override void Persist([NotNull] string name, AccessControlSections includeSections = Task.defaultAccessControlSections) - { - using (var ts = new TaskService()) - { - var task = ts.GetTask(name); - Persist(task, includeSections); - } - } - } + public enum TaskRights + { + /// Specifies the right to exert full control over a task folder or task, and to modify access control and audit rules. This value represents the right to do anything with a task and is the combination of all rights in this enumeration. + FullControl = 0x1f01ff, + /// Specifies the right to create tasks and folders, and to add or remove data from tasks. This right includes the following rights: . + Write = 0x120116, + /// Specifies the right to open and copy folders or tasks as read-only. This right includes the following rights: . + Read = 0x120089, + /// Specifies the right run tasks. This right includes the following rights: . + Execute = 0x120089, + /// The right to wait on a task. + Synchronize = 0x100000, + /// The right to change the owner of a task. + TakeOwnership = 0x80000, + /// Specifies the right to change the security and audit rules associated with a task or folder. + ChangePermissions = 0x40000, + /// The right to open and copy the access rules and audit rules for a task. + ReadPermissions = 0x20000, + /// The right to delete a folder or task. + Delete = 0x10000, + /// Specifies the right to open and write file system attributes to a folder or file. This does not include the ability to write data, extended attributes, or access and audit rules. + WriteAttributes = 0x100, + /// Specifies the right to open and copy file system attributes from a folder or task. For example, this value specifies the right to view the file creation or modified date. This does not include the right to read data, extended file system attributes, or access and audit rules. + ReadAttributes = 0x80, + /// Specifies the right to delete a folder and any tasks contained within that folder. + DeleteChild = 0x40, + /// Specifies the right to run a task. + ExecuteFile = 0x20, + /// Specifies the right to open and write extended file system attributes to a folder or file. This does not include the ability to write data, attributes, or access and audit rules. + WriteExtendedAttributes = 0x10, + /// Specifies the right to open and copy extended system attributes from a folder or task. For example, this value specifies the right to view author and content information. This does not include the right to read data, system attributes, or access and audit rules. + ReadExtendedAttributes = 8, + /// Specifies the right to append data to the end of a file. + AppendData = 4, + /// Specifies the right to open and write to a file or folder. This does not include the right to open and write file system attributes, extended file system attributes, or access and audit rules. + WriteData = 2, + /// Specifies the right to open and copy a task or folder. This does not include the right to read file system attributes, extended file system attributes, or access and audit rules. + ReadData = 1, + } + + /// + /// Represents a set of access rights allowed or denied for a user or group. This class cannot be inherited. + /// + public sealed class TaskAccessRule : AccessRule + { + /// + /// Initializes a new instance of the class, specifying the user or group the rule applies to, the access rights, and whether the specified access rights are allowed or denied. + /// + /// The user or group the rule applies to. Must be of type or a type such as that can be converted to type . + /// A bitwise combination of values specifying the rights allowed or denied. + /// One of the values specifying whether the rights are allowed or denied. + public TaskAccessRule([NotNull] IdentityReference identity, TaskRights eventRights, AccessControlType type) + : this(identity, (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, type) + { + } + + /// + /// Initializes a new instance of the class, specifying the name of the user or group the rule applies to, the access rights, and whether the specified access rights are allowed or denied. + /// + /// The name of the user or group the rule applies to. + /// A bitwise combination of values specifying the rights allowed or denied. + /// One of the values specifying whether the rights are allowed or denied. + public TaskAccessRule([NotNull] string identity, TaskRights eventRights, AccessControlType type) + : this(new NTAccount(identity), (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, type) + { + } + + private TaskAccessRule([NotNull] IdentityReference identity, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) + : base(identity, accessMask, isInherited, inheritanceFlags, propagationFlags, type) + { + } + + /// + /// Gets the rights allowed or denied by the access rule. + /// + /// + /// A bitwise combination of values indicating the rights allowed or denied by the access rule. + /// + public TaskRights TaskRights => (TaskRights)AccessMask; + } + + /// + /// Represents a set of access rights to be audited for a user or group. This class cannot be inherited. + /// + public sealed class TaskAuditRule : AuditRule + { + /// + /// Initializes a new instance of the class, specifying the user or group to audit, the rights to audit, and whether to audit success, failure, or both. + /// + /// The user or group the rule applies to. Must be of type or a type such as that can be converted to type . + /// A bitwise combination of values specifying the kinds of access to audit. + /// The audit flags. + public TaskAuditRule([NotNull] IdentityReference identity, TaskRights eventRights, AuditFlags flags) + : this(identity, (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, flags) + { + } + + internal TaskAuditRule([NotNull] IdentityReference identity, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) + : base(identity, accessMask, isInherited, inheritanceFlags, propagationFlags, flags) + { + } + + /// + /// Gets the access rights affected by the audit rule. + /// + /// + /// A bitwise combination of values that indicates the rights affected by the audit rule. + /// + /// objects are immutable. You can create a new audit rule representing a different user, different rights, or a different combination of AuditFlags values, but you cannot modify an existing audit rule. + public TaskRights TaskRights => (TaskRights)AccessMask; + } + + /// + /// Represents the Windows access control security for a Task Scheduler task. This class cannot be inherited. + /// + /// + /// A TaskSecurity object specifies access rights for a Task Scheduler task, and also specifies how access attempts are audited. Access rights to the task are expressed as rules, with each access rule represented by a object. Each auditing rule is represented by a object. + /// This mirrors the underlying Windows security system, in which each securable object has at most one discretionary access control list (DACL) that controls access to the secured object, and at most one system access control list (SACL) that specifies which access attempts are audited. The DACL and SACL are ordered lists of access control entries (ACE) that specify access and auditing for users and groups. A or object might represent more than one ACE. + /// Note + /// A object can represent a local task or a Task Scheduler task. Windows access control security is meaningful only for Task Scheduler tasks. + /// The TaskSecurity, , and classes hide the implementation details of ACLs and ACEs. They allow you to ignore the seventeen different ACE types and the complexity of correctly maintaining inheritance and propagation of access rights. These objects are also designed to prevent the following common access control errors: + /// + /// Creating a security descriptor with a null DACL. A null reference to a DACL allows any user to add access rules to an object, potentially creating a denial-of-service attack. A new TaskSecurity object always starts with an empty DACL, which denies all access for all users. + /// Violating the canonical ordering of ACEs. If the ACE list in the DACL is not kept in the canonical order, users might inadvertently be given access to the secured object. For example, denied access rights must always appear before allowed access rights. TaskSecurity objects maintain the correct order internally. + /// Manipulating security descriptor flags, which should be under resource manager control only. + /// Creating invalid combinations of ACE flags. + /// Manipulating inherited ACEs. Inheritance and propagation are handled by the resource manager, in response to changes you make to access and audit rules. + /// Inserting meaningless ACEs into ACLs. + /// + /// The only capabilities not supported by the .NET security objects are dangerous activities that should be avoided by the majority of application developers, such as the following: + /// + /// Low-level tasks that are normally performed by the resource manager. + /// Adding or removing access control entries in ways that do not maintain the canonical ordering. + /// + /// To modify Windows access control security for a task, use the method to get the TaskSecurity object. Modify the security object by adding and removing rules, and then use the method to reattach it. + /// Important: Changes you make to a TaskSecurity object do not affect the access levels of the task until you call the method to assign the altered security object to the task. + /// To copy access control security from one task to another, use the method to get a TaskSecurity object representing the access and audit rules for the first task, then use the method, or a constructor that accepts a TaskSecurity object, to assign those rules to the second task. + /// Users with an investment in the security descriptor definition language (SDDL) can use the method to set access rules for a task, and the method to obtain a string that represents the access rules in SDDL format. This is not recommended for new development. + /// + public sealed class TaskSecurity : CommonObjectSecurity + { + /// + /// Initializes a new instance of the class with default values. + /// + public TaskSecurity() + : base(false) + { + } + + /// + /// Initializes a new instance of the class with the specified sections of the access control security rules from the specified task. + /// + /// The task. + /// The sections of the ACL to retrieve. + public TaskSecurity([NotNull] Task task, AccessControlSections sections = Task.defaultAccessControlSections) + : base(false) + { + SetSecurityDescriptorSddlForm(task.GetSecurityDescriptorSddlForm(Convert(sections)), sections); + this.CanonicalizeAccessRules(); + } + + /// + /// Initializes a new instance of the class with the specified sections of the access control security rules from the specified task. + /// + /// The folder. + /// The sections of the ACL to retrieve. + public TaskSecurity([NotNull] TaskFolder folder, AccessControlSections sections = Task.defaultAccessControlSections) + : base(false) + { + SetSecurityDescriptorSddlForm(folder.GetSecurityDescriptorSddlForm(Convert(sections)), sections); + this.CanonicalizeAccessRules(); + } + + /// + /// Gets the enumeration that the class uses to represent access rights. + /// + /// A object representing the enumeration. + public override Type AccessRightType => typeof(TaskRights); + + /// + /// Gets the type that the TaskSecurity class uses to represent access rules. + /// + /// A object representing the class. + public override Type AccessRuleType => typeof(TaskAccessRule); + + /// + /// Gets the type that the TaskSecurity class uses to represent audit rules. + /// + /// A object representing the class. + public override Type AuditRuleType => typeof(TaskAuditRule); + + /// + /// Gets a object that represent the default access rights. + /// + /// The default task security. + public static TaskSecurity DefaultTaskSecurity + { + get + { + var ret = new TaskSecurity(); + ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), TaskRights.FullControl, AccessControlType.Allow)); + ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null), TaskRights.Read | TaskRights.Write | TaskRights.Execute, AccessControlType.Allow)); + ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null), TaskRights.Read, AccessControlType.Allow)); + ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), TaskRights.Read, AccessControlType.Allow)); + ret.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null), TaskRights.Read, AccessControlType.Allow)); + return ret; + } + } + + /// + /// Creates a new access control rule for the specified user, with the specified access rights, access control, and flags. + /// + /// An that identifies the user or group the rule applies to. + /// A bitwise combination of values specifying the access rights to allow or deny, cast to an integer. + /// Meaningless for tasks, because they have no hierarchy. + /// Meaningless for tasks, because they have no hierarchy. + /// Meaningless for tasks, because they have no hierarchy. + /// One of the values specifying whether the rights are allowed or denied. + /// + /// The object that this method creates. + /// + public override AccessRule AccessRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) => new TaskAccessRule(identityReference, (TaskRights)accessMask, type); + + /// + /// Searches for a matching rule with which the new rule can be merged. If none are found, adds the new rule. + /// + /// The access control rule to add. + public void AddAccessRule([NotNull] TaskAccessRule rule) + { + base.AddAccessRule(rule); + } + + /// + /// Searches for an audit rule with which the new rule can be merged. If none are found, adds the new rule. + /// + /// The audit rule to add. The user specified by this rule determines the search. + public void AddAuditRule([NotNull] TaskAuditRule rule) + { + base.AddAuditRule(rule); + } + + /// + /// Creates a new audit rule, specifying the user the rule applies to, the access rights to audit, and the outcome that triggers the audit rule. + /// + /// An that identifies the user or group the rule applies to. + /// A bitwise combination of values specifying the access rights to audit, cast to an integer. + /// Meaningless for tasks, because they have no hierarchy. + /// Meaningless for tasks, because they have no hierarchy. + /// Meaningless for tasks, because they have no hierarchy. + /// One of the values specifying whether to audit successful access, failed access, or both. + /// + /// A object representing the specified audit rule for the specified user. The return type of the method is the base class, , but the return value can be cast safely to the derived class. + /// + public override AuditRule AuditRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) => new TaskAuditRule(identityReference, accessMask, isInherited, inheritanceFlags, propagationFlags, flags); + + /// + /// Searches for an access control rule with the same user and (allow or deny) as the specified rule, and with compatible inheritance and propagation flags; if such a rule is found, the rights contained in the specified access rule are removed from it. + /// + /// A that specifies the user and to search for, and a set of inheritance and propagation flags that a matching rule, if found, must be compatible with. Specifies the rights to remove from the compatible rule, if found. + /// true if a compatible rule is found; otherwise false. + public bool RemoveAccessRule([NotNull] TaskAccessRule rule) => base.RemoveAccessRule(rule); + + /// + /// Searches for all access control rules with the same user and (allow or deny) as the specified rule and, if found, removes them. + /// + /// A that specifies the user and to search for, and a set of inheritance and propagation flags that a matching rule, if found, must be compatible with. Any rights specified by this rule are ignored. + public void RemoveAccessRuleAll([NotNull] TaskAccessRule rule) + { + base.RemoveAccessRuleAll(rule); + } + + /// + /// Searches for an access control rule that exactly matches the specified rule and, if found, removes it. + /// + /// The to remove. + public void RemoveAccessRuleSpecific([NotNull] TaskAccessRule rule) + { + base.RemoveAccessRuleSpecific(rule); + } + + /// + /// Searches for an audit control rule with the same user as the specified rule, and with compatible inheritance and propagation flags; if a compatible rule is found, the rights contained in the specified rule are removed from it. + /// + /// A that specifies the user to search for, and a set of inheritance and propagation flags that a matching rule, if found, must be compatible with. Specifies the rights to remove from the compatible rule, if found. + /// true if a compatible rule is found; otherwise false. + public bool RemoveAuditRule([NotNull] TaskAuditRule rule) => base.RemoveAuditRule(rule); + + /// + /// Searches for all audit rules with the same user as the specified rule and, if found, removes them. + /// + /// A that specifies the user to search for. Any rights specified by this rule are ignored. + public void RemoveAuditRuleAll(TaskAuditRule rule) + { + base.RemoveAuditRuleAll(rule); + } + + /// + /// Searches for an audit rule that exactly matches the specified rule and, if found, removes it. + /// + /// The to remove. + public void RemoveAuditRuleSpecific([NotNull] TaskAuditRule rule) + { + base.RemoveAuditRuleSpecific(rule); + } + + /// + /// Removes all access control rules with the same user as the specified rule, regardless of , and then adds the specified rule. + /// + /// The to add. The user specified by this rule determines the rules to remove before this rule is added. + public void ResetAccessRule([NotNull] TaskAccessRule rule) + { + base.ResetAccessRule(rule); + } + + /// + /// Removes all access control rules with the same user and (allow or deny) as the specified rule, and then adds the specified rule. + /// + /// The to add. The user and of this rule determine the rules to remove before this rule is added. + public void SetAccessRule([NotNull] TaskAccessRule rule) + { + base.SetAccessRule(rule); + } + + /// + /// Removes all audit rules with the same user as the specified rule, regardless of the value, and then adds the specified rule. + /// + /// The to add. The user specified by this rule determines the rules to remove before this rule is added. + public void SetAuditRule([NotNull] TaskAuditRule rule) + { + base.SetAuditRule(rule); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() => GetSecurityDescriptorSddlForm(Task.defaultAccessControlSections); + + private static SecurityInfos Convert(AccessControlSections si) + { + SecurityInfos ret = 0; + if ((si & AccessControlSections.Audit) != 0) + ret |= SecurityInfos.SystemAcl; + if ((si & AccessControlSections.Access) != 0) + ret |= SecurityInfos.DiscretionaryAcl; + if ((si & AccessControlSections.Group) != 0) + ret |= SecurityInfos.Group; + if ((si & AccessControlSections.Owner) != 0) + ret |= SecurityInfos.Owner; + return ret; + } + + private static AccessControlSections Convert(SecurityInfos si) + { + AccessControlSections ret = AccessControlSections.None; + if ((si & SecurityInfos.SystemAcl) != 0) + ret |= AccessControlSections.Audit; + if ((si & SecurityInfos.DiscretionaryAcl) != 0) + ret |= AccessControlSections.Access; + if ((si & SecurityInfos.Group) != 0) + ret |= AccessControlSections.Group; + if ((si & SecurityInfos.Owner) != 0) + ret |= AccessControlSections.Owner; + return ret; + } + + private AccessControlSections GetAccessControlSectionsFromChanges() + { + AccessControlSections none = AccessControlSections.None; + if (AccessRulesModified) + { + none = AccessControlSections.Access; + } + if (AuditRulesModified) + { + none |= AccessControlSections.Audit; + } + if (OwnerModified) + { + none |= AccessControlSections.Owner; + } + if (GroupModified) + { + none |= AccessControlSections.Group; + } + return none; + } + + /// + /// Saves the specified sections of the security descriptor associated with this object to permanent storage. We recommend that the values of the parameters passed to the constructor and persist methods be identical. + /// + /// The task used to retrieve the persisted information. + /// One of the enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save. + [SecurityCritical] + internal void Persist([NotNull] Task task, AccessControlSections includeSections = Task.defaultAccessControlSections) + { + WriteLock(); + try + { + AccessControlSections accessControlSectionsFromChanges = GetAccessControlSectionsFromChanges(); + if (accessControlSectionsFromChanges != AccessControlSections.None) + { + task.SetSecurityDescriptorSddlForm(GetSecurityDescriptorSddlForm(accessControlSectionsFromChanges)); + OwnerModified = GroupModified = AccessRulesModified = AuditRulesModified = false; + } + } + finally + { + WriteUnlock(); + } + } + + /// + /// Saves the specified sections of the security descriptor associated with this object to permanent storage. We recommend that the values of the parameters passed to the constructor and persist methods be identical. + /// + /// The task folder used to retrieve the persisted information. + /// One of the enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save. + [SecurityCritical] + internal void Persist([NotNull] TaskFolder folder, AccessControlSections includeSections = Task.defaultAccessControlSections) + { + WriteLock(); + try + { + AccessControlSections accessControlSectionsFromChanges = GetAccessControlSectionsFromChanges(); + if (accessControlSectionsFromChanges != AccessControlSections.None) + { + folder.SetSecurityDescriptorSddlForm(GetSecurityDescriptorSddlForm(accessControlSectionsFromChanges)); + OwnerModified = GroupModified = AccessRulesModified = AuditRulesModified = false; + } + } + finally + { + WriteUnlock(); + } + } + + /// + /// Saves the specified sections of the security descriptor associated with this object to permanent storage. We recommend that the values of the parameters passed to the constructor and persist methods be identical. For more information, see Remarks. + /// + /// The name used to retrieve the persisted information. + /// One of the enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save. + protected override void Persist([NotNull] string name, AccessControlSections includeSections = Task.defaultAccessControlSections) + { + using (var ts = new TaskService()) + { + var task = ts.GetTask(name); + Persist(task, includeSections); + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskService.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskService.cs index d39962cfa..0f30e521d 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskService.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TaskService.cs @@ -2,1166 +2,1163 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using winPEAS.TaskScheduler.V1; using winPEAS.TaskScheduler.V2; namespace winPEAS.TaskScheduler { - /// - /// Quick simple trigger types for the - /// method. - /// - public enum QuickTriggerType - { - /// At boot. - Boot, - - /// On system idle. - Idle, - - /// At logon of any user. - Logon, - - /// When the task is registered. - TaskRegistration, - - /// Hourly, starting now. - Hourly, - - /// Daily, starting now. - Daily, - - /// Weekly, starting now. - Weekly, - - /// Monthly, starting now. - Monthly - } - - /// - /// Known versions of the native Task Scheduler library. This can be used as a decoder for the - /// and values. - /// - public static class TaskServiceVersion - { - /// Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). - [Description("Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000).")] - public static readonly Version V1_1 = new Version(1, 1); - - /// Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). - [Description("Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008).")] - public static readonly Version V1_2 = new Version(1, 2); - - /// Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). - [Description("Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2).")] - public static readonly Version V1_3 = new Version(1, 3); - - /// Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). - [Description("Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012).")] - public static readonly Version V1_4 = new Version(1, 4); - - /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). - [Description("Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016).")] - public static readonly Version V1_5 = new Version(1, 5); - - /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016 post build 1703). - [Description("Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016 post build 1703).")] - public static readonly Version V1_6 = new Version(1, 6); - } - - /// Provides access to the Task Scheduler service for managing registered tasks. - [Description("Provides access to the Task Scheduler service.")] - [ToolboxItem(true), Serializable] - public sealed partial class TaskService : Component, ISupportInitialize, System.Runtime.Serialization.ISerializable - { - internal static readonly bool LibraryIsV2 = Environment.OSVersion.Version.Major >= 6; - internal static readonly Guid PowerShellActionGuid = new Guid("dab4c1e3-cd12-46f1-96fc-3981143c9bab"); - private static Guid CLSID_Ctask = typeof(CTask).GUID; - private static Guid IID_ITask = typeof(ITask).GUID; - [ThreadStatic] - private static TaskService instance; - private static Version osLibVer; - - internal ITaskScheduler v1TaskScheduler; - internal ITaskService v2TaskService; - private bool connecting; - private bool forceV1; - private bool initializing; - private Version maxVer; - private bool maxVerSet; - private string targetServer; - private bool targetServerSet; - private string userDomain; - private bool userDomainSet; - private string userName; - private bool userNameSet; - private string userPassword; - private bool userPasswordSet; - private WindowsImpersonatedIdentity v1Impersonation; - - /// Creates a new instance of a TaskService connecting to the local machine as the current user. - public TaskService() - { - ResetHighestSupportedVersion(); - Connect(); - } - - /// Initializes a new instance of the class. - /// - /// The name of the computer that you want to connect to. If the this parameter is empty, then this will connect to the local computer. - /// - /// - /// The user name that is used during the connection to the computer. If the user is not specified, then the current token is used. - /// - /// The domain of the user specified in the parameter. - /// - /// The password that is used to connect to the computer. If the user name and password are not specified, then the current token is used. - /// - /// If set to true force Task Scheduler 1.0 compatibility. - public TaskService(string targetServer, string userName = null, string accountDomain = null, string password = null, bool forceV1 = false) - { - BeginInit(); - TargetServer = targetServer; - UserName = userName; - UserAccountDomain = accountDomain; - UserPassword = password; - this.forceV1 = forceV1; - ResetHighestSupportedVersion(); - EndInit(); - } - - private TaskService([NotNull] System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - { - BeginInit(); - TargetServer = (string)info.GetValue("TargetServer", typeof(string)); - UserName = (string)info.GetValue("UserName", typeof(string)); - UserAccountDomain = (string)info.GetValue("UserAccountDomain", typeof(string)); - UserPassword = (string)info.GetValue("UserPassword", typeof(string)); - forceV1 = (bool)info.GetValue("forceV1", typeof(bool)); - ResetHighestSupportedVersion(); - EndInit(); - } - - /// Delegate for methods that support update calls during COM handler execution. - /// The percentage of completion (0 to 100). - /// An optional message. - public delegate void ComHandlerUpdate(short percentage, string message); - - /// Occurs when the Task Scheduler is connected to the local or remote target. - public event EventHandler ServiceConnected; - - /// Occurs when the Task Scheduler is disconnected from the local or remote target. - public event EventHandler ServiceDisconnected; - - /// Gets a local instance of the using the current user's credentials. - /// Local user instance. - public static TaskService Instance - { - get - { - if (instance is null) - { - instance = new TaskService(); - instance.ServiceDisconnected += Instance_ServiceDisconnected; - } - return instance; - } - } - - /// - /// Gets the library version. This is the highest version supported by the local library. Tasks cannot be created using any - /// compatibility level higher than this version. - /// - /// The library version. - /// - /// The following table list the various versions and their host operating system: - /// - /// - /// Version - /// Operating System - /// - /// - /// 1.1 - /// Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). - /// - /// - /// 1.2 - /// Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). - /// - /// - /// 1.3 - /// Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). - /// - /// - /// 1.4 - /// Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). - /// - /// - /// 1.5 - /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). - /// - /// - /// 1.6 - /// Task Scheduler 2.4 (Windows® 10 Version 1703, Windows Server™ 2016 Version 1703). - /// - /// - /// - [Browsable(false)] - public static Version LibraryVersion { get; } = Instance.HighestSupportedVersion; - - /// - /// Gets or sets a value indicating whether to allow tasks from later OS versions with new properties to be retrieved as read only tasks. - /// - /// true if allow read only tasks; otherwise, false. - [DefaultValue(false), Category("Behavior"), Description("Allow tasks from later OS versions with new properties to be retrieved as read only tasks.")] - public bool AllowReadOnlyTasks { get; set; } - - /// Gets the name of the domain to which the computer is connected. - [Browsable(false)] - [DefaultValue(null)] - [Obsolete("This property has been superseded by the UserAccountDomin property and may not be available in future releases.")] - public string ConnectedDomain - { - get - { - if (v2TaskService != null) - return v2TaskService.ConnectedDomain; - var parts = v1Impersonation.Name.Split('\\'); - if (parts.Length == 2) - return parts[0]; - return string.Empty; - } - } - - /// Gets the name of the user that is connected to the Task Scheduler service. - [Browsable(false)] - [DefaultValue(null)] - [Obsolete("This property has been superseded by the UserName property and may not be available in future releases.")] - public string ConnectedUser - { - get - { - if (v2TaskService != null) - return v2TaskService.ConnectedUser; - var parts = v1Impersonation.Name.Split('\\'); - if (parts.Length == 2) - return parts[1]; - return parts[0]; - } - } - - /// Gets the highest version of Task Scheduler that a computer supports. - /// - /// The following table list the various versions and their host operating system: - /// - /// - /// Version - /// Operating System - /// - /// - /// 1.1 - /// Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). - /// - /// - /// 1.2 - /// Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). - /// - /// - /// 1.3 - /// Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). - /// - /// - /// 1.4 - /// Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). - /// - /// - /// 1.5 - /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). - /// - /// - /// 1.6 - /// Task Scheduler 2.4 (Windows® 10 Version 1703, Windows Server™ 2016 Version 1703). - /// - /// - /// - [Category("Data"), TypeConverter(typeof(VersionConverter)), Description("Highest version of library that should be used.")] - public Version HighestSupportedVersion - { - get => maxVer; - set - { - if (value > GetLibraryVersionFromLocalOS()) - throw new ArgumentOutOfRangeException(nameof(HighestSupportedVersion), @"The value of HighestSupportedVersion cannot exceed that of the underlying Windows version library."); - maxVer = value; - maxVerSet = true; - var localForceV1 = value <= TaskServiceVersion.V1_1; - if (localForceV1 == forceV1) return; - forceV1 = localForceV1; - Connect(); - } - } - - /// Gets the root ("\") folder. For Task Scheduler 1.0, this is the only folder. - [Browsable(false)] - public TaskFolder RootFolder => GetFolder(TaskFolder.rootString); - - /// Gets or sets the name of the computer that is running the Task Scheduler service that the user is connected to. - [Category("Data"), DefaultValue(null), Description("The name of the computer to connect to.")] - public string TargetServer - { - get => ShouldSerializeTargetServer() ? targetServer : null; - set - { - if (value == null || value.Trim() == string.Empty) value = null; - if (string.Compare(value, targetServer, StringComparison.OrdinalIgnoreCase) != 0) - { - targetServerSet = true; - targetServer = value; - Connect(); - } - } - } - - /// Gets or sets the user account domain to be used when connecting to the . - /// The user account domain. - [Category("Data"), DefaultValue(null), Description("The user account domain to be used when connecting.")] - public string UserAccountDomain - { - get => ShouldSerializeUserAccountDomain() ? userDomain : null; - set - { - if (value == null || value.Trim() == string.Empty) value = null; - if (string.Compare(value, userDomain, StringComparison.OrdinalIgnoreCase) != 0) - { - userDomainSet = true; - userDomain = value; - Connect(); - } - } - } - - /// Gets or sets the user name to be used when connecting to the . - /// The user name. - [Category("Data"), DefaultValue(null), Description("The user name to be used when connecting.")] - public string UserName - { - get => ShouldSerializeUserName() ? userName : null; - set - { - if (value == null || value.Trim() == string.Empty) value = null; - if (string.Compare(value, userName, StringComparison.OrdinalIgnoreCase) != 0) - { - userNameSet = true; - userName = value; - Connect(); - } - } - } - - /// Gets or sets the user password to be used when connecting to the . - /// The user password. - [Category("Data"), DefaultValue(null), Description("The user password to be used when connecting.")] - public string UserPassword - { - get => userPassword; - set - { - if (value == null || value.Trim() == string.Empty) value = null; - if (string.CompareOrdinal(value, userPassword) != 0) - { - userPasswordSet = true; - userPassword = value; - Connect(); - } - } - } - - /// Gets a which enumerates all the tasks in all folders. - /// A for all instances. - [Browsable(false)] - public System.Collections.Generic.IEnumerable AllTasks => RootFolder.AllTasks; - - /// Gets a Boolean value that indicates if you are connected to the Task Scheduler service. - [Browsable(false)] - public bool Connected => v2TaskService != null && v2TaskService.Connected || v1TaskScheduler != null; - - /// - /// Gets the connection token for this instance. This token is thread safe and can be used to create new - /// instances on other threads using the static method. - /// - /// The connection token. - public ConnectionToken Token => - ConnectionDataManager.TokenFromInstance(TargetServer, UserName, UserAccountDomain, UserPassword, forceV1); - - /// Gets a value indicating whether the component can raise an event. - protected override bool CanRaiseEvents { get; } = false; - - /// - /// Creates a new instance from a token. Given that a TaskService instance is thread specific, this is the - /// preferred method for multi-thread creation or asynchronous method parameters. - /// - /// The token. - /// A instance valid for the thread calling this method. - public static TaskService CreateFromToken(ConnectionToken token) => ConnectionDataManager.InstanceFromToken(token); - - /// Gets a formatted string that tells the Task Scheduler to retrieve a string from a resource .dll file. - /// The path to the .dll file that contains the resource. - /// The identifier for the resource text (typically a negative number). - /// A string in the format of $(@ [dllPath], [resourceId]). - /// - /// For example, the setting this property value to $(@ %SystemRoot%\System32\ResourceName.dll, -101) will set the property to the - /// value of the resource text with an identifier equal to -101 in the %SystemRoot%\System32\ResourceName.dll file. - /// - public static string GetDllResourceString([NotNull] string dllPath, int resourceId) => $"$(@ {dllPath}, {resourceId})"; - - /// - /// Runs an action that is defined via a COM handler. COM CLSID must be registered to an object that implements the - /// interface. - /// - /// The CLSID of the COM object. - /// An optional string passed to the COM object at startup. - /// The number of milliseconds to wait or -1 for indefinitely. - /// - /// An optional delegate that is called when the COM object calls the - /// method. - /// - /// The value set by the COM object via a call to the method. - public static int RunComHandlerAction(Guid clsid, string data = null, int millisecondsTimeout = -1, ComHandlerUpdate onUpdate = null) - { - var thread = new ComHandlerThread(clsid, data, millisecondsTimeout, onUpdate, null); - thread.Start().Join(); - return thread.ReturnCode; - } - - /// - /// Runs an action that is defined via a COM handler. COM CLSID must be registered to an object that implements the - /// interface. - /// - /// The CLSID of the COM object. - /// The action to run on thread completion. - /// An optional string passed to the COM object at startup. - /// The number of milliseconds to wait or -1 for indefinitely. - /// - /// An optional delegate that is called when the COM object calls the - /// method. - /// - public static void RunComHandlerActionAsync(Guid clsid, Action onComplete, string data = null, int millisecondsTimeout = -1, ComHandlerUpdate onUpdate = null) => new ComHandlerThread(clsid, data, millisecondsTimeout, onUpdate, onComplete).Start(); - - /// Adds or updates an Automatic Maintenance Task on the connected machine. - /// Name of the task with full path. - /// The amount of time the task needs once executed during regular Automatic maintenance. - /// - /// The amount of time after which the Task Scheduler attempts to run the task during emergency Automatic maintenance, if the task - /// failed to complete during regular Automatic Maintenance. - /// - /// The path to an executable file. - /// The arguments associated with the command-line operation. - /// - /// The directory that contains either the executable file or the files that are used by the executable file. - /// - /// A instance of the Automatic Maintenance Task. - /// - /// Automatic Maintenance tasks are only supported on Windows 8/Server 2012 and later. - /// - public Task AddAutomaticMaintenanceTask([NotNull] string taskPathAndName, TimeSpan period, TimeSpan deadline, string executablePath, string arguments = null, string workingDirectory = null) - { - if (HighestSupportedVersion.Minor < 4) - throw new InvalidOperationException("Automatic Maintenance tasks are only supported on Windows 8/Server 2012 and later."); - var td = NewTask(); - td.Settings.UseUnifiedSchedulingEngine = true; - td.Settings.MaintenanceSettings.Period = period; - td.Settings.MaintenanceSettings.Deadline = deadline; - td.Actions.Add(executablePath, arguments, workingDirectory); - // The task needs to grant explicit FRFX to LOCAL SERVICE (A;;FRFX;;;LS) - return RootFolder.RegisterTaskDefinition(taskPathAndName, td, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.InteractiveToken, "D:P(A;;FA;;;BA)(A;;FA;;;SY)(A;;FRFX;;;LS)"); - } - - /// Creates a new task, registers the task, and returns the instance. - /// - /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value - /// that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot - /// be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// - /// The to determine when to run the task. - /// The to determine what happens when the task is triggered. - /// The user credentials used to register the task. - /// The password for the userId used to register the task. - /// - /// A value that defines what logon technique is used to run the registered task. - /// - /// The task description. - /// A instance of the registered task. - /// - /// This method is shorthand for creating a new TaskDescription, adding a trigger and action, and then registering it in the root folder. - /// - /// - /// - /// - /// - /// - public Task AddTask([NotNull] string path, [NotNull] Trigger trigger, [NotNull] Action action, string userId = null, string password = null, TaskLogonType logonType = TaskLogonType.InteractiveToken, string description = null) - { - var td = NewTask(); - if (!string.IsNullOrEmpty(description)) - td.RegistrationInfo.Description = description; - - // Create a trigger that will fire the task at a specific date and time - td.Triggers.Add(trigger); - - // Create an action that will launch Notepad whenever the trigger fires - td.Actions.Add(action); - - // Register the task in the root folder - return RootFolder.RegisterTaskDefinition(path, td, TaskCreation.CreateOrUpdate, userId, password, logonType); - } - - /// Creates a new task, registers the task, and returns the instance. - /// - /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value - /// that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot - /// be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// - /// The to determine when to run the task. - /// The executable path. - /// The arguments (optional). Value can be NULL. - /// The user credentials used to register the task. - /// The password for the userId used to register the task. - /// - /// A value that defines what logon technique is used to run the registered task. - /// - /// The task description. - /// A instance of the registered task. - /// - /// - /// - /// - /// - public Task AddTask([NotNull] string path, QuickTriggerType trigger, [NotNull] string exePath, string arguments = null, string userId = null, string password = null, TaskLogonType logonType = TaskLogonType.InteractiveToken, string description = null) - { - // Create a trigger based on quick trigger - Trigger newTrigger; - switch (trigger) - { - case QuickTriggerType.Boot: - newTrigger = new BootTrigger(); - break; - - case QuickTriggerType.Idle: - newTrigger = new IdleTrigger(); - break; - - case QuickTriggerType.Logon: - newTrigger = new LogonTrigger(); - break; - - case QuickTriggerType.TaskRegistration: - newTrigger = new RegistrationTrigger(); - break; - - case QuickTriggerType.Hourly: - newTrigger = new DailyTrigger { Repetition = new RepetitionPattern(TimeSpan.FromHours(1), TimeSpan.FromDays(1)) }; - break; - - case QuickTriggerType.Daily: - newTrigger = new DailyTrigger(); - break; - - case QuickTriggerType.Weekly: - newTrigger = new WeeklyTrigger(); - break; - - case QuickTriggerType.Monthly: - newTrigger = new MonthlyTrigger(); - break; - - default: - throw new ArgumentOutOfRangeException(nameof(trigger), trigger, null); - } - - return AddTask(path, newTrigger, new Action.ExecAction(exePath, arguments), userId, password, logonType, description); - } - - /// Signals the object that initialization is starting. - public void BeginInit() => initializing = true; - - /// Signals the object that initialization is complete. - public void EndInit() - { - initializing = false; - Connect(); - } - - /// Determines whether the specified , is equal to this instance. - /// The to compare with this instance. - /// true if the specified is equal to this instance; otherwise, false. - public override bool Equals(object obj) - { - var tsobj = obj as TaskService; - if (tsobj != null) - return tsobj.TargetServer == TargetServer && tsobj.UserAccountDomain == UserAccountDomain && tsobj.UserName == UserName && tsobj.UserPassword == UserPassword && tsobj.forceV1 == forceV1; - return base.Equals(obj); - } - - /// Finds all tasks matching a name or standard wildcards. - /// Name of the task in regular expression form. - /// if set to true search all sub folders. - /// An array of containing all tasks matching . - public Task[] FindAllTasks(System.Text.RegularExpressions.Regex name, bool searchAllFolders = true) - { - var results = new System.Collections.Generic.List(); - FindTaskInFolder(RootFolder, name, ref results, searchAllFolders); - return results.ToArray(); - } - - /// Finds all tasks matching a name or standard wildcards. - /// The filter used to determine tasks to select. - /// if set to true search all sub folders. - /// An array of containing all tasks matching . - public Task[] FindAllTasks(Predicate filter, bool searchAllFolders = true) - { - if (filter == null) filter = t => true; - var results = new System.Collections.Generic.List(); - FindTaskInFolder(RootFolder, filter, ref results, searchAllFolders); - return results.ToArray(); - } - - /// Finds a task given a name and standard wildcards. - /// The task name. This can include the wildcards * or ?. - /// if set to true search all sub folders. - /// A if one matches , otherwise NULL. - public Task FindTask([NotNull] string name, bool searchAllFolders = true) - { - var results = FindAllTasks(new Wildcard(name), searchAllFolders); - if (results.Length > 0) - return results[0]; - return null; - } - - /// Gets the event log for this instance. - /// (Optional) The task path if only the events for a single task are desired. - /// A instance. - public TaskEventLog GetEventLog(string taskPath = null) => new TaskEventLog(TargetServer, taskPath, UserAccountDomain, UserName, UserPassword); - - /// Gets the path to a folder of registered tasks. - /// - /// The path to the folder to retrieve. Do not use a backslash following the last folder name in the path. The root task folder is - /// specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character - /// cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. - /// - /// instance for the requested folder or null if was unrecognized. - /// - /// Folder other than the root (\) was requested on a system not supporting Task Scheduler 2.0. - /// - public TaskFolder GetFolder(string folderName) - { - TaskFolder f = null; - if (v2TaskService != null) - { - if (string.IsNullOrEmpty(folderName)) folderName = TaskFolder.rootString; - try - { - var ifld = v2TaskService.GetFolder(folderName); - if (ifld != null) - f = new TaskFolder(this, ifld); - } - catch (System.IO.DirectoryNotFoundException) { } - catch (System.IO.FileNotFoundException) { } - } - else if (folderName == TaskFolder.rootString || string.IsNullOrEmpty(folderName)) - f = new TaskFolder(this); - else - throw new NotV1SupportedException("Folder other than the root (\\) was requested on a system only supporting Task Scheduler 1.0."); - return f; - } - - /// Returns a hash code for this instance. - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() => new { A = TargetServer, B = UserAccountDomain, C = UserName, D = UserPassword, E = forceV1 }.GetHashCode(); - - /// Gets a collection of running tasks. - /// True to include hidden tasks. - /// instance with the list of running tasks. - public RunningTaskCollection GetRunningTasks(bool includeHidden = true) - { - if (v2TaskService != null) - try - { - return new RunningTaskCollection(this, v2TaskService.GetRunningTasks(includeHidden ? 1 : 0)); - } - catch { } - return new RunningTaskCollection(this); - } - - /// Gets the task with the specified path. - /// The task path. - /// - /// The instance matching the , if found. If not found, this method returns null. - /// - public Task GetTask([NotNull] string taskPath) - { - Task t = null; - if (v2TaskService != null) - { - var iTask = GetTask(v2TaskService, taskPath); - if (iTask != null) - t = Task.CreateTask(this, iTask); - } - else - { - taskPath = System.IO.Path.GetFileNameWithoutExtension(taskPath); - var iTask = GetTask(v1TaskScheduler, taskPath); - if (iTask != null) - t = new Task(this, iTask); - } - return t; - } - - /// - /// Returns an empty task definition object to be filled in with settings and properties and then registered using the - /// method. - /// - /// A instance for setting properties. - public TaskDefinition NewTask() - { - if (v2TaskService != null) - return new TaskDefinition(v2TaskService.NewTask(0)); - var v1Name = "Temp" + Guid.NewGuid().ToString("B"); - return new TaskDefinition(v1TaskScheduler.NewWorkItem(v1Name, CLSID_Ctask, IID_ITask), v1Name); - } - - /// Returns a populated with the properties defined in an XML file. - /// The XML file to use as input. - /// A instance. - /// Importing from an XML file is only supported under Task Scheduler 2.0. - public TaskDefinition NewTaskFromFile([NotNull] string xmlFile) - { - var td = NewTask(); - td.XmlText = System.IO.File.ReadAllText(xmlFile); - return td; - } - - /// Starts the Task Scheduler UI for the OS hosting the assembly if the session is running in interactive mode. - public void StartSystemTaskSchedulerManager() - { - if (Environment.UserInteractive) - System.Diagnostics.Process.Start("control.exe", "schedtasks"); - } - - [System.Security.SecurityCritical] - void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - { - info.AddValue("TargetServer", TargetServer, typeof(string)); - info.AddValue("UserName", UserName, typeof(string)); - info.AddValue("UserAccountDomain", UserAccountDomain, typeof(string)); - info.AddValue("UserPassword", UserPassword, typeof(string)); - info.AddValue("forceV1", forceV1, typeof(bool)); - } - - internal static IRegisteredTask GetTask([NotNull] ITaskService iSvc, [NotNull] string name) - { - ITaskFolder fld = null; - try - { - fld = iSvc.GetFolder("\\"); - return fld.GetTask(name); - } - catch - { - return null; - } - finally - { - if (fld != null) Marshal.ReleaseComObject(fld); - } - } - - internal static ITask GetTask([NotNull] ITaskScheduler iSvc, [NotNull] string name) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentNullException(nameof(name)); - try - { - return iSvc.Activate(name, IID_ITask); - } - catch (UnauthorizedAccessException) - { - // TODO: Take ownership of the file and try again - throw; - } - catch (ArgumentException) - { - return iSvc.Activate(name + ".job", IID_ITask); - } - catch (FileNotFoundException) - { - return null; - } - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - if (v2TaskService != null) - { - try - { - Marshal.ReleaseComObject(v2TaskService); - } - catch { } - v2TaskService = null; - } - if (v1TaskScheduler != null) - { - try - { - Marshal.ReleaseComObject(v1TaskScheduler); - } - catch { } - v1TaskScheduler = null; - } - if (v1Impersonation != null) - { - v1Impersonation.Dispose(); - v1Impersonation = null; - } - if (!connecting) - ServiceDisconnected?.Invoke(this, EventArgs.Empty); - base.Dispose(disposing); - } - - private static Version GetLibraryVersionFromLocalOS() - { - if (osLibVer == null) - { - if (Environment.OSVersion.Version.Major < 6) - osLibVer = TaskServiceVersion.V1_1; - else - { - if (Environment.OSVersion.Version.Minor == 0) - osLibVer = TaskServiceVersion.V1_2; - else if (Environment.OSVersion.Version.Minor == 1) - osLibVer = TaskServiceVersion.V1_3; - else - { - try - { - var fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(Path.Combine(Environment.SystemDirectory, "taskschd.dll")); - if (fvi.FileBuildPart > 9600 && fvi.FileBuildPart <= 14393) - osLibVer = TaskServiceVersion.V1_5; - else if (fvi.FileBuildPart >= 15063) - osLibVer = TaskServiceVersion.V1_6; - else // fvi.FileBuildPart <= 9600 - osLibVer = TaskServiceVersion.V1_4; - } - catch { /* ignored */ }; - } - } - - if (osLibVer == null) - throw new NotSupportedException(@"The Task Scheduler library version for this system cannot be determined."); - } - return osLibVer; - } - - private static void Instance_ServiceDisconnected(object sender, EventArgs e) => instance?.Connect(); - - /// Connects this instance of the class to a running Task Scheduler. - private void Connect() - { - ResetUnsetProperties(); - - if (!initializing && !DesignMode) - { - if (!string.IsNullOrEmpty(userDomain) && !string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(userPassword) || string.IsNullOrEmpty(userDomain) && string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(userPassword)) - { - // Clear stuff if already connected - connecting = true; - Dispose(true); - - if (LibraryIsV2 && !forceV1) - { - v2TaskService = new ITaskService(); - if (!string.IsNullOrEmpty(targetServer)) - { - // Check to ensure character only server name. (Suggested by bigsan) - if (targetServer.StartsWith(@"\")) - targetServer = targetServer.TrimStart('\\'); - // Make sure null is provided for local machine to compensate for a native library oddity (Found by ctrollen) - if (targetServer.Equals(Environment.MachineName, StringComparison.CurrentCultureIgnoreCase)) - targetServer = null; - } - else - targetServer = null; - v2TaskService.Connect(targetServer, userName, userDomain, userPassword); - targetServer = v2TaskService.TargetServer; - userName = v2TaskService.ConnectedUser; - userDomain = v2TaskService.ConnectedDomain; - maxVer = GetV2Version(); - } - else - { - v1Impersonation = new WindowsImpersonatedIdentity(userName, userDomain, userPassword); - v1TaskScheduler = new ITaskScheduler(); - if (!string.IsNullOrEmpty(targetServer)) - { - // Check to ensure UNC format for server name. (Suggested by bigsan) - if (!targetServer.StartsWith(@"\\")) - targetServer = @"\\" + targetServer; - } - else - targetServer = null; - v1TaskScheduler.SetTargetComputer(targetServer); - targetServer = v1TaskScheduler.GetTargetComputer(); - maxVer = TaskServiceVersion.V1_1; - } - ServiceConnected?.Invoke(this, EventArgs.Empty); - connecting = false; - } - else - { - throw new ArgumentException("A username, password, and domain must be provided."); - } - } - } - - /// Finds the task in folder. - /// The folder. - /// The wildcard expression to compare task names with. - /// The results. - /// if set to true recurse folders. - /// True if any tasks are found, False if not. - private bool FindTaskInFolder([NotNull] TaskFolder fld, System.Text.RegularExpressions.Regex taskName, ref System.Collections.Generic.List results, bool recurse = true) - { - results.AddRange(fld.GetTasks(taskName)); - - if (recurse) - { - foreach (var f in fld.SubFolders) - { - if (FindTaskInFolder(f, taskName, ref results)) - return true; - } - } - return false; - } - - /// Finds the task in folder. - /// The folder. - /// The filter to use when looking for tasks. - /// The results. - /// if set to true recurse folders. - /// True if any tasks are found, False if not. - private bool FindTaskInFolder([NotNull] TaskFolder fld, Predicate filter, ref System.Collections.Generic.List results, bool recurse = true) - { - foreach (var t in fld.GetTasks()) - try - { - if (filter(t)) - results.Add(t); - } - catch - { - System.Diagnostics.Debug.WriteLine($"Unable to evaluate filter for task '{t.Path}'."); - } - - if (recurse) - { - foreach (var f in fld.SubFolders) - { - if (FindTaskInFolder(f, filter, ref results)) - return true; - } - } - return false; - } - - private Version GetV2Version() - { - var v = v2TaskService.HighestVersion; - return new Version((int)(v >> 16), (int)(v & 0x0000FFFF)); - } - - private void ResetHighestSupportedVersion() => maxVer = Connected ? (v2TaskService != null ? GetV2Version() : TaskServiceVersion.V1_1) : GetLibraryVersionFromLocalOS(); - - private void ResetUnsetProperties() - { - if (!maxVerSet) ResetHighestSupportedVersion(); - if (!targetServerSet) targetServer = null; - if (!userDomainSet) userDomain = null; - if (!userNameSet) userName = null; - if (!userPasswordSet) userPassword = null; - } - - private bool ShouldSerializeHighestSupportedVersion() => LibraryIsV2 && maxVer <= TaskServiceVersion.V1_1; - - private bool ShouldSerializeTargetServer() => targetServer != null && !targetServer.Trim('\\').Equals(Environment.MachineName.Trim('\\'), StringComparison.InvariantCultureIgnoreCase); - - private bool ShouldSerializeUserAccountDomain() => userDomain != null && !userDomain.Equals(Environment.UserDomainName, StringComparison.InvariantCultureIgnoreCase); - - private bool ShouldSerializeUserName() => userName != null && !userName.Equals(Environment.UserName, StringComparison.InvariantCultureIgnoreCase); - - /// - /// Represents a valid, connected session to a Task Scheduler instance. This token is thread-safe and should be the means of passing - /// information about a between threads. - /// - public struct ConnectionToken - { - internal int token; - - internal ConnectionToken(int value) => token = value; - } - - // Manages the list of tokens and associated data - private static class ConnectionDataManager - { - public static List connections = new List() { new ConnectionData(null) }; - - public static TaskService InstanceFromToken(ConnectionToken token) - { - ConnectionData data; - lock (connections) - { - data = connections[token.token < connections.Count ? token.token : 0]; - } - return new TaskService(data.TargetServer, data.UserName, data.UserAccountDomain, data.UserPassword, data.ForceV1); - } - - public static ConnectionToken TokenFromInstance(string targetServer, string userName = null, - string accountDomain = null, string password = null, bool forceV1 = false) - { - lock (connections) - { - var newData = new ConnectionData(targetServer, userName, accountDomain, password, forceV1); - for (var i = 0; i < connections.Count; i++) - { - if (connections[i].Equals(newData)) - return new ConnectionToken(i); - } - connections.Add(newData); - return new ConnectionToken(connections.Count - 1); - } - } - } - - private class ComHandlerThread - { - public int ReturnCode; - private readonly System.Threading.AutoResetEvent completed = new System.Threading.AutoResetEvent(false); - private readonly string Data; - private readonly Type objType; - private readonly TaskHandlerStatus status; - private readonly int Timeout; - - public ComHandlerThread(Guid clsid, string data, int millisecondsTimeout, ComHandlerUpdate onUpdate, Action onComplete) - { - objType = Type.GetTypeFromCLSID(clsid, true); - Data = data; - Timeout = millisecondsTimeout; - status = new TaskHandlerStatus(i => - { - completed.Set(); - onComplete?.Invoke(i); - }, onUpdate); - } - - public System.Threading.Thread Start() - { - var t = new System.Threading.Thread(ThreadProc); - t.Start(); - return t; - } - - private void ThreadProc() - { - completed.Reset(); - object obj = null; - try { obj = Activator.CreateInstance(objType); } catch { } - if (obj == null) return; - ITaskHandler taskHandler = null; - try { taskHandler = (ITaskHandler)obj; } catch { } - try - { - if (taskHandler != null) - { - taskHandler.Start(status, Data); - completed.WaitOne(Timeout); - taskHandler.Stop(out ReturnCode); - } - } - finally - { - if (taskHandler != null) - Marshal.ReleaseComObject(taskHandler); - Marshal.ReleaseComObject(obj); - } - } - - private class TaskHandlerStatus : ITaskHandlerStatus - { - private readonly Action OnCompleted; - private readonly ComHandlerUpdate OnUpdate; - - public TaskHandlerStatus(Action onCompleted, ComHandlerUpdate onUpdate) - { - OnCompleted = onCompleted; - OnUpdate = onUpdate; - } - - public void TaskCompleted([In, MarshalAs(UnmanagedType.Error)] int taskErrCode) => OnCompleted?.Invoke(taskErrCode); - - public void UpdateStatus([In] short percentComplete, [In, MarshalAs(UnmanagedType.BStr)] string statusMessage) => OnUpdate?.Invoke(percentComplete, statusMessage); - } - } - - // This private class holds information needed to create a new TaskService instance - private class ConnectionData : IEquatable - { - public bool ForceV1; - public string TargetServer, UserAccountDomain, UserName, UserPassword; - - public ConnectionData(string targetServer, string userName = null, string accountDomain = null, string password = null, bool forceV1 = false) - { - TargetServer = targetServer; - UserAccountDomain = accountDomain; - UserName = userName; - UserPassword = password; - ForceV1 = forceV1; - } - - public bool Equals(ConnectionData other) => string.Equals(TargetServer, other.TargetServer, StringComparison.InvariantCultureIgnoreCase) && - string.Equals(UserAccountDomain, other.UserAccountDomain, StringComparison.InvariantCultureIgnoreCase) && - string.Equals(UserName, other.UserName, StringComparison.InvariantCultureIgnoreCase) && - string.Equals(UserPassword, other.UserPassword, StringComparison.InvariantCultureIgnoreCase) && - ForceV1 == other.ForceV1; - } - - private class VersionConverter : TypeConverter - { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - return true; - return base.CanConvertFrom(context, sourceType); - } - - public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) - { - var s = value as string; - return s != null ? new Version(s) : base.ConvertFrom(context, culture, value); - } - } - } + /// + /// Quick simple trigger types for the + /// method. + /// + public enum QuickTriggerType + { + /// At boot. + Boot, + + /// On system idle. + Idle, + + /// At logon of any user. + Logon, + + /// When the task is registered. + TaskRegistration, + + /// Hourly, starting now. + Hourly, + + /// Daily, starting now. + Daily, + + /// Weekly, starting now. + Weekly, + + /// Monthly, starting now. + Monthly + } + + /// + /// Known versions of the native Task Scheduler library. This can be used as a decoder for the + /// and values. + /// + public static class TaskServiceVersion + { + /// Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). + [Description("Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000).")] + public static readonly Version V1_1 = new Version(1, 1); + + /// Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). + [Description("Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008).")] + public static readonly Version V1_2 = new Version(1, 2); + + /// Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). + [Description("Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2).")] + public static readonly Version V1_3 = new Version(1, 3); + + /// Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). + [Description("Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012).")] + public static readonly Version V1_4 = new Version(1, 4); + + /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). + [Description("Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016).")] + public static readonly Version V1_5 = new Version(1, 5); + + /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016 post build 1703). + [Description("Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016 post build 1703).")] + public static readonly Version V1_6 = new Version(1, 6); + } + + /// Provides access to the Task Scheduler service for managing registered tasks. + [Description("Provides access to the Task Scheduler service.")] + [ToolboxItem(true), Serializable] + public sealed partial class TaskService : Component, ISupportInitialize, System.Runtime.Serialization.ISerializable + { + internal static readonly bool LibraryIsV2 = Environment.OSVersion.Version.Major >= 6; + internal static readonly Guid PowerShellActionGuid = new Guid("dab4c1e3-cd12-46f1-96fc-3981143c9bab"); + private static Guid CLSID_Ctask = typeof(CTask).GUID; + private static Guid IID_ITask = typeof(ITask).GUID; + [ThreadStatic] + private static TaskService instance; + private static Version osLibVer; + + internal ITaskScheduler v1TaskScheduler; + internal ITaskService v2TaskService; + private bool connecting; + private bool forceV1; + private bool initializing; + private Version maxVer; + private bool maxVerSet; + private string targetServer; + private bool targetServerSet; + private string userDomain; + private bool userDomainSet; + private string userName; + private bool userNameSet; + private string userPassword; + private bool userPasswordSet; + private WindowsImpersonatedIdentity v1Impersonation; + + /// Creates a new instance of a TaskService connecting to the local machine as the current user. + public TaskService() + { + ResetHighestSupportedVersion(); + Connect(); + } + + /// Initializes a new instance of the class. + /// + /// The name of the computer that you want to connect to. If the this parameter is empty, then this will connect to the local computer. + /// + /// + /// The user name that is used during the connection to the computer. If the user is not specified, then the current token is used. + /// + /// The domain of the user specified in the parameter. + /// + /// The password that is used to connect to the computer. If the user name and password are not specified, then the current token is used. + /// + /// If set to true force Task Scheduler 1.0 compatibility. + public TaskService(string targetServer, string userName = null, string accountDomain = null, string password = null, bool forceV1 = false) + { + BeginInit(); + TargetServer = targetServer; + UserName = userName; + UserAccountDomain = accountDomain; + UserPassword = password; + this.forceV1 = forceV1; + ResetHighestSupportedVersion(); + EndInit(); + } + + private TaskService([NotNull] System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + { + BeginInit(); + TargetServer = (string)info.GetValue("TargetServer", typeof(string)); + UserName = (string)info.GetValue("UserName", typeof(string)); + UserAccountDomain = (string)info.GetValue("UserAccountDomain", typeof(string)); + UserPassword = (string)info.GetValue("UserPassword", typeof(string)); + forceV1 = (bool)info.GetValue("forceV1", typeof(bool)); + ResetHighestSupportedVersion(); + EndInit(); + } + + /// Delegate for methods that support update calls during COM handler execution. + /// The percentage of completion (0 to 100). + /// An optional message. + public delegate void ComHandlerUpdate(short percentage, string message); + + /// Occurs when the Task Scheduler is connected to the local or remote target. + public event EventHandler ServiceConnected; + + /// Occurs when the Task Scheduler is disconnected from the local or remote target. + public event EventHandler ServiceDisconnected; + + /// Gets a local instance of the using the current user's credentials. + /// Local user instance. + public static TaskService Instance + { + get + { + if (instance is null) + { + instance = new TaskService(); + instance.ServiceDisconnected += Instance_ServiceDisconnected; + } + return instance; + } + } + + /// + /// Gets the library version. This is the highest version supported by the local library. Tasks cannot be created using any + /// compatibility level higher than this version. + /// + /// The library version. + /// + /// The following table list the various versions and their host operating system: + /// + /// + /// Version + /// Operating System + /// + /// + /// 1.1 + /// Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). + /// + /// + /// 1.2 + /// Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). + /// + /// + /// 1.3 + /// Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). + /// + /// + /// 1.4 + /// Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). + /// + /// + /// 1.5 + /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). + /// + /// + /// 1.6 + /// Task Scheduler 2.4 (Windows® 10 Version 1703, Windows Server™ 2016 Version 1703). + /// + /// + /// + [Browsable(false)] + public static Version LibraryVersion { get; } = Instance.HighestSupportedVersion; + + /// + /// Gets or sets a value indicating whether to allow tasks from later OS versions with new properties to be retrieved as read only tasks. + /// + /// true if allow read only tasks; otherwise, false. + [DefaultValue(false), Category("Behavior"), Description("Allow tasks from later OS versions with new properties to be retrieved as read only tasks.")] + public bool AllowReadOnlyTasks { get; set; } + + /// Gets the name of the domain to which the computer is connected. + [Browsable(false)] + [DefaultValue(null)] + [Obsolete("This property has been superseded by the UserAccountDomin property and may not be available in future releases.")] + public string ConnectedDomain + { + get + { + if (v2TaskService != null) + return v2TaskService.ConnectedDomain; + var parts = v1Impersonation.Name.Split('\\'); + if (parts.Length == 2) + return parts[0]; + return string.Empty; + } + } + + /// Gets the name of the user that is connected to the Task Scheduler service. + [Browsable(false)] + [DefaultValue(null)] + [Obsolete("This property has been superseded by the UserName property and may not be available in future releases.")] + public string ConnectedUser + { + get + { + if (v2TaskService != null) + return v2TaskService.ConnectedUser; + var parts = v1Impersonation.Name.Split('\\'); + if (parts.Length == 2) + return parts[1]; + return parts[0]; + } + } + + /// Gets the highest version of Task Scheduler that a computer supports. + /// + /// The following table list the various versions and their host operating system: + /// + /// + /// Version + /// Operating System + /// + /// + /// 1.1 + /// Task Scheduler 1.0 (Windows Server™ 2003, Windows® XP, or Windows® 2000). + /// + /// + /// 1.2 + /// Task Scheduler 2.0 (Windows Vista™, Windows Server™ 2008). + /// + /// + /// 1.3 + /// Task Scheduler 2.1 (Windows® 7, Windows Server™ 2008 R2). + /// + /// + /// 1.4 + /// Task Scheduler 2.2 (Windows® 8.x, Windows Server™ 2012). + /// + /// + /// 1.5 + /// Task Scheduler 2.3 (Windows® 10, Windows Server™ 2016). + /// + /// + /// 1.6 + /// Task Scheduler 2.4 (Windows® 10 Version 1703, Windows Server™ 2016 Version 1703). + /// + /// + /// + [Category("Data"), TypeConverter(typeof(VersionConverter)), Description("Highest version of library that should be used.")] + public Version HighestSupportedVersion + { + get => maxVer; + set + { + if (value > GetLibraryVersionFromLocalOS()) + throw new ArgumentOutOfRangeException(nameof(HighestSupportedVersion), @"The value of HighestSupportedVersion cannot exceed that of the underlying Windows version library."); + maxVer = value; + maxVerSet = true; + var localForceV1 = value <= TaskServiceVersion.V1_1; + if (localForceV1 == forceV1) return; + forceV1 = localForceV1; + Connect(); + } + } + + /// Gets the root ("\") folder. For Task Scheduler 1.0, this is the only folder. + [Browsable(false)] + public TaskFolder RootFolder => GetFolder(TaskFolder.rootString); + + /// Gets or sets the name of the computer that is running the Task Scheduler service that the user is connected to. + [Category("Data"), DefaultValue(null), Description("The name of the computer to connect to.")] + public string TargetServer + { + get => ShouldSerializeTargetServer() ? targetServer : null; + set + { + if (value == null || value.Trim() == string.Empty) value = null; + if (string.Compare(value, targetServer, StringComparison.OrdinalIgnoreCase) != 0) + { + targetServerSet = true; + targetServer = value; + Connect(); + } + } + } + + /// Gets or sets the user account domain to be used when connecting to the . + /// The user account domain. + [Category("Data"), DefaultValue(null), Description("The user account domain to be used when connecting.")] + public string UserAccountDomain + { + get => ShouldSerializeUserAccountDomain() ? userDomain : null; + set + { + if (value == null || value.Trim() == string.Empty) value = null; + if (string.Compare(value, userDomain, StringComparison.OrdinalIgnoreCase) != 0) + { + userDomainSet = true; + userDomain = value; + Connect(); + } + } + } + + /// Gets or sets the user name to be used when connecting to the . + /// The user name. + [Category("Data"), DefaultValue(null), Description("The user name to be used when connecting.")] + public string UserName + { + get => ShouldSerializeUserName() ? userName : null; + set + { + if (value == null || value.Trim() == string.Empty) value = null; + if (string.Compare(value, userName, StringComparison.OrdinalIgnoreCase) != 0) + { + userNameSet = true; + userName = value; + Connect(); + } + } + } + + /// Gets or sets the user password to be used when connecting to the . + /// The user password. + [Category("Data"), DefaultValue(null), Description("The user password to be used when connecting.")] + public string UserPassword + { + get => userPassword; + set + { + if (value == null || value.Trim() == string.Empty) value = null; + if (string.CompareOrdinal(value, userPassword) != 0) + { + userPasswordSet = true; + userPassword = value; + Connect(); + } + } + } + + /// Gets a which enumerates all the tasks in all folders. + /// A for all instances. + [Browsable(false)] + public System.Collections.Generic.IEnumerable AllTasks => RootFolder.AllTasks; + + /// Gets a Boolean value that indicates if you are connected to the Task Scheduler service. + [Browsable(false)] + public bool Connected => v2TaskService != null && v2TaskService.Connected || v1TaskScheduler != null; + + /// + /// Gets the connection token for this instance. This token is thread safe and can be used to create new + /// instances on other threads using the static method. + /// + /// The connection token. + public ConnectionToken Token => + ConnectionDataManager.TokenFromInstance(TargetServer, UserName, UserAccountDomain, UserPassword, forceV1); + + /// Gets a value indicating whether the component can raise an event. + protected override bool CanRaiseEvents { get; } = false; + + /// + /// Creates a new instance from a token. Given that a TaskService instance is thread specific, this is the + /// preferred method for multi-thread creation or asynchronous method parameters. + /// + /// The token. + /// A instance valid for the thread calling this method. + public static TaskService CreateFromToken(ConnectionToken token) => ConnectionDataManager.InstanceFromToken(token); + + /// Gets a formatted string that tells the Task Scheduler to retrieve a string from a resource .dll file. + /// The path to the .dll file that contains the resource. + /// The identifier for the resource text (typically a negative number). + /// A string in the format of $(@ [dllPath], [resourceId]). + /// + /// For example, the setting this property value to $(@ %SystemRoot%\System32\ResourceName.dll, -101) will set the property to the + /// value of the resource text with an identifier equal to -101 in the %SystemRoot%\System32\ResourceName.dll file. + /// + public static string GetDllResourceString([NotNull] string dllPath, int resourceId) => $"$(@ {dllPath}, {resourceId})"; + + /// + /// Runs an action that is defined via a COM handler. COM CLSID must be registered to an object that implements the + /// interface. + /// + /// The CLSID of the COM object. + /// An optional string passed to the COM object at startup. + /// The number of milliseconds to wait or -1 for indefinitely. + /// + /// An optional delegate that is called when the COM object calls the + /// method. + /// + /// The value set by the COM object via a call to the method. + public static int RunComHandlerAction(Guid clsid, string data = null, int millisecondsTimeout = -1, ComHandlerUpdate onUpdate = null) + { + var thread = new ComHandlerThread(clsid, data, millisecondsTimeout, onUpdate, null); + thread.Start().Join(); + return thread.ReturnCode; + } + + /// + /// Runs an action that is defined via a COM handler. COM CLSID must be registered to an object that implements the + /// interface. + /// + /// The CLSID of the COM object. + /// The action to run on thread completion. + /// An optional string passed to the COM object at startup. + /// The number of milliseconds to wait or -1 for indefinitely. + /// + /// An optional delegate that is called when the COM object calls the + /// method. + /// + public static void RunComHandlerActionAsync(Guid clsid, Action onComplete, string data = null, int millisecondsTimeout = -1, ComHandlerUpdate onUpdate = null) => new ComHandlerThread(clsid, data, millisecondsTimeout, onUpdate, onComplete).Start(); + + /// Adds or updates an Automatic Maintenance Task on the connected machine. + /// Name of the task with full path. + /// The amount of time the task needs once executed during regular Automatic maintenance. + /// + /// The amount of time after which the Task Scheduler attempts to run the task during emergency Automatic maintenance, if the task + /// failed to complete during regular Automatic Maintenance. + /// + /// The path to an executable file. + /// The arguments associated with the command-line operation. + /// + /// The directory that contains either the executable file or the files that are used by the executable file. + /// + /// A instance of the Automatic Maintenance Task. + /// + /// Automatic Maintenance tasks are only supported on Windows 8/Server 2012 and later. + /// + public Task AddAutomaticMaintenanceTask([NotNull] string taskPathAndName, TimeSpan period, TimeSpan deadline, string executablePath, string arguments = null, string workingDirectory = null) + { + if (HighestSupportedVersion.Minor < 4) + throw new InvalidOperationException("Automatic Maintenance tasks are only supported on Windows 8/Server 2012 and later."); + var td = NewTask(); + td.Settings.UseUnifiedSchedulingEngine = true; + td.Settings.MaintenanceSettings.Period = period; + td.Settings.MaintenanceSettings.Deadline = deadline; + td.Actions.Add(executablePath, arguments, workingDirectory); + // The task needs to grant explicit FRFX to LOCAL SERVICE (A;;FRFX;;;LS) + return RootFolder.RegisterTaskDefinition(taskPathAndName, td, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.InteractiveToken, "D:P(A;;FA;;;BA)(A;;FA;;;SY)(A;;FRFX;;;LS)"); + } + + /// Creates a new task, registers the task, and returns the instance. + /// + /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value + /// that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot + /// be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// + /// The to determine when to run the task. + /// The to determine what happens when the task is triggered. + /// The user credentials used to register the task. + /// The password for the userId used to register the task. + /// + /// A value that defines what logon technique is used to run the registered task. + /// + /// The task description. + /// A instance of the registered task. + /// + /// This method is shorthand for creating a new TaskDescription, adding a trigger and action, and then registering it in the root folder. + /// + /// + /// + /// + /// + /// + public Task AddTask([NotNull] string path, [NotNull] Trigger trigger, [NotNull] Action action, string userId = null, string password = null, TaskLogonType logonType = TaskLogonType.InteractiveToken, string description = null) + { + var td = NewTask(); + if (!string.IsNullOrEmpty(description)) + td.RegistrationInfo.Description = description; + + // Create a trigger that will fire the task at a specific date and time + td.Triggers.Add(trigger); + + // Create an action that will launch Notepad whenever the trigger fires + td.Actions.Add(action); + + // Register the task in the root folder + return RootFolder.RegisterTaskDefinition(path, td, TaskCreation.CreateOrUpdate, userId, password, logonType); + } + + /// Creates a new task, registers the task, and returns the instance. + /// + /// The task name. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value + /// that is created by the Task Scheduler service. A task name cannot begin or end with a space character. The '.' character cannot + /// be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// + /// The to determine when to run the task. + /// The executable path. + /// The arguments (optional). Value can be NULL. + /// The user credentials used to register the task. + /// The password for the userId used to register the task. + /// + /// A value that defines what logon technique is used to run the registered task. + /// + /// The task description. + /// A instance of the registered task. + /// + /// + /// + /// + /// + public Task AddTask([NotNull] string path, QuickTriggerType trigger, [NotNull] string exePath, string arguments = null, string userId = null, string password = null, TaskLogonType logonType = TaskLogonType.InteractiveToken, string description = null) + { + // Create a trigger based on quick trigger + Trigger newTrigger; + switch (trigger) + { + case QuickTriggerType.Boot: + newTrigger = new BootTrigger(); + break; + + case QuickTriggerType.Idle: + newTrigger = new IdleTrigger(); + break; + + case QuickTriggerType.Logon: + newTrigger = new LogonTrigger(); + break; + + case QuickTriggerType.TaskRegistration: + newTrigger = new RegistrationTrigger(); + break; + + case QuickTriggerType.Hourly: + newTrigger = new DailyTrigger { Repetition = new RepetitionPattern(TimeSpan.FromHours(1), TimeSpan.FromDays(1)) }; + break; + + case QuickTriggerType.Daily: + newTrigger = new DailyTrigger(); + break; + + case QuickTriggerType.Weekly: + newTrigger = new WeeklyTrigger(); + break; + + case QuickTriggerType.Monthly: + newTrigger = new MonthlyTrigger(); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(trigger), trigger, null); + } + + return AddTask(path, newTrigger, new Action.ExecAction(exePath, arguments), userId, password, logonType, description); + } + + /// Signals the object that initialization is starting. + public void BeginInit() => initializing = true; + + /// Signals the object that initialization is complete. + public void EndInit() + { + initializing = false; + Connect(); + } + + /// Determines whether the specified , is equal to this instance. + /// The to compare with this instance. + /// true if the specified is equal to this instance; otherwise, false. + public override bool Equals(object obj) + { + var tsobj = obj as TaskService; + if (tsobj != null) + return tsobj.TargetServer == TargetServer && tsobj.UserAccountDomain == UserAccountDomain && tsobj.UserName == UserName && tsobj.UserPassword == UserPassword && tsobj.forceV1 == forceV1; + return base.Equals(obj); + } + + /// Finds all tasks matching a name or standard wildcards. + /// Name of the task in regular expression form. + /// if set to true search all sub folders. + /// An array of containing all tasks matching . + public Task[] FindAllTasks(System.Text.RegularExpressions.Regex name, bool searchAllFolders = true) + { + var results = new System.Collections.Generic.List(); + FindTaskInFolder(RootFolder, name, ref results, searchAllFolders); + return results.ToArray(); + } + + /// Finds all tasks matching a name or standard wildcards. + /// The filter used to determine tasks to select. + /// if set to true search all sub folders. + /// An array of containing all tasks matching . + public Task[] FindAllTasks(Predicate filter, bool searchAllFolders = true) + { + if (filter == null) filter = t => true; + var results = new System.Collections.Generic.List(); + FindTaskInFolder(RootFolder, filter, ref results, searchAllFolders); + return results.ToArray(); + } + + /// Finds a task given a name and standard wildcards. + /// The task name. This can include the wildcards * or ?. + /// if set to true search all sub folders. + /// A if one matches , otherwise NULL. + public Task FindTask([NotNull] string name, bool searchAllFolders = true) + { + var results = FindAllTasks(new Wildcard(name), searchAllFolders); + if (results.Length > 0) + return results[0]; + return null; + } + + /// Gets the event log for this instance. + /// (Optional) The task path if only the events for a single task are desired. + /// A instance. + public TaskEventLog GetEventLog(string taskPath = null) => new TaskEventLog(TargetServer, taskPath, UserAccountDomain, UserName, UserPassword); + + /// Gets the path to a folder of registered tasks. + /// + /// The path to the folder to retrieve. Do not use a backslash following the last folder name in the path. The root task folder is + /// specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character + /// cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path. + /// + /// instance for the requested folder or null if was unrecognized. + /// + /// Folder other than the root (\) was requested on a system not supporting Task Scheduler 2.0. + /// + public TaskFolder GetFolder(string folderName) + { + TaskFolder f = null; + if (v2TaskService != null) + { + if (string.IsNullOrEmpty(folderName)) folderName = TaskFolder.rootString; + try + { + var ifld = v2TaskService.GetFolder(folderName); + if (ifld != null) + f = new TaskFolder(this, ifld); + } + catch (System.IO.DirectoryNotFoundException) { } + catch (System.IO.FileNotFoundException) { } + } + else if (folderName == TaskFolder.rootString || string.IsNullOrEmpty(folderName)) + f = new TaskFolder(this); + else + throw new NotV1SupportedException("Folder other than the root (\\) was requested on a system only supporting Task Scheduler 1.0."); + return f; + } + + /// Returns a hash code for this instance. + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + public override int GetHashCode() => new { A = TargetServer, B = UserAccountDomain, C = UserName, D = UserPassword, E = forceV1 }.GetHashCode(); + + /// Gets a collection of running tasks. + /// True to include hidden tasks. + /// instance with the list of running tasks. + public RunningTaskCollection GetRunningTasks(bool includeHidden = true) + { + if (v2TaskService != null) + try + { + return new RunningTaskCollection(this, v2TaskService.GetRunningTasks(includeHidden ? 1 : 0)); + } + catch { } + return new RunningTaskCollection(this); + } + + /// Gets the task with the specified path. + /// The task path. + /// + /// The instance matching the , if found. If not found, this method returns null. + /// + public Task GetTask([NotNull] string taskPath) + { + Task t = null; + if (v2TaskService != null) + { + var iTask = GetTask(v2TaskService, taskPath); + if (iTask != null) + t = Task.CreateTask(this, iTask); + } + else + { + taskPath = Path.GetFileNameWithoutExtension(taskPath); + var iTask = GetTask(v1TaskScheduler, taskPath); + if (iTask != null) + t = new Task(this, iTask); + } + return t; + } + + /// + /// Returns an empty task definition object to be filled in with settings and properties and then registered using the + /// method. + /// + /// A instance for setting properties. + public TaskDefinition NewTask() + { + if (v2TaskService != null) + return new TaskDefinition(v2TaskService.NewTask(0)); + var v1Name = "Temp" + Guid.NewGuid().ToString("B"); + return new TaskDefinition(v1TaskScheduler.NewWorkItem(v1Name, CLSID_Ctask, IID_ITask), v1Name); + } + + /// Returns a populated with the properties defined in an XML file. + /// The XML file to use as input. + /// A instance. + /// Importing from an XML file is only supported under Task Scheduler 2.0. + public TaskDefinition NewTaskFromFile([NotNull] string xmlFile) + { + var td = NewTask(); + td.XmlText = File.ReadAllText(xmlFile); + return td; + } + + /// Starts the Task Scheduler UI for the OS hosting the assembly if the session is running in interactive mode. + public void StartSystemTaskSchedulerManager() + { + if (Environment.UserInteractive) + System.Diagnostics.Process.Start("control.exe", "schedtasks"); + } + + [System.Security.SecurityCritical] + void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + { + info.AddValue("TargetServer", TargetServer, typeof(string)); + info.AddValue("UserName", UserName, typeof(string)); + info.AddValue("UserAccountDomain", UserAccountDomain, typeof(string)); + info.AddValue("UserPassword", UserPassword, typeof(string)); + info.AddValue("forceV1", forceV1, typeof(bool)); + } + + internal static IRegisteredTask GetTask([NotNull] ITaskService iSvc, [NotNull] string name) + { + ITaskFolder fld = null; + try + { + fld = iSvc.GetFolder("\\"); + return fld.GetTask(name); + } + catch + { + return null; + } + finally + { + if (fld != null) Marshal.ReleaseComObject(fld); + } + } + + internal static ITask GetTask([NotNull] ITaskScheduler iSvc, [NotNull] string name) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException(nameof(name)); + try + { + return iSvc.Activate(name, IID_ITask); + } + catch (UnauthorizedAccessException) + { + // TODO: Take ownership of the file and try again + throw; + } + catch (ArgumentException) + { + return iSvc.Activate(name + ".job", IID_ITask); + } + catch (FileNotFoundException) + { + return null; + } + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + if (v2TaskService != null) + { + try + { + Marshal.ReleaseComObject(v2TaskService); + } + catch { } + v2TaskService = null; + } + if (v1TaskScheduler != null) + { + try + { + Marshal.ReleaseComObject(v1TaskScheduler); + } + catch { } + v1TaskScheduler = null; + } + if (v1Impersonation != null) + { + v1Impersonation.Dispose(); + v1Impersonation = null; + } + if (!connecting) + ServiceDisconnected?.Invoke(this, EventArgs.Empty); + base.Dispose(disposing); + } + + private static Version GetLibraryVersionFromLocalOS() + { + if (osLibVer == null) + { + if (Environment.OSVersion.Version.Major < 6) + osLibVer = TaskServiceVersion.V1_1; + else + { + if (Environment.OSVersion.Version.Minor == 0) + osLibVer = TaskServiceVersion.V1_2; + else if (Environment.OSVersion.Version.Minor == 1) + osLibVer = TaskServiceVersion.V1_3; + else + { + try + { + var fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(Path.Combine(Environment.SystemDirectory, "taskschd.dll")); + if (fvi.FileBuildPart > 9600 && fvi.FileBuildPart <= 14393) + osLibVer = TaskServiceVersion.V1_5; + else if (fvi.FileBuildPart >= 15063) + osLibVer = TaskServiceVersion.V1_6; + else // fvi.FileBuildPart <= 9600 + osLibVer = TaskServiceVersion.V1_4; + } + catch { /* ignored */ }; + } + } + + if (osLibVer == null) + throw new NotSupportedException(@"The Task Scheduler library version for this system cannot be determined."); + } + return osLibVer; + } + + private static void Instance_ServiceDisconnected(object sender, EventArgs e) => instance?.Connect(); + + /// Connects this instance of the class to a running Task Scheduler. + private void Connect() + { + ResetUnsetProperties(); + + if (!initializing && !DesignMode) + { + if (!string.IsNullOrEmpty(userDomain) && !string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(userPassword) || string.IsNullOrEmpty(userDomain) && string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(userPassword)) + { + // Clear stuff if already connected + connecting = true; + Dispose(true); + + if (LibraryIsV2 && !forceV1) + { + v2TaskService = new ITaskService(); + if (!string.IsNullOrEmpty(targetServer)) + { + // Check to ensure character only server name. (Suggested by bigsan) + if (targetServer.StartsWith(@"\")) + targetServer = targetServer.TrimStart('\\'); + // Make sure null is provided for local machine to compensate for a native library oddity (Found by ctrollen) + if (targetServer.Equals(Environment.MachineName, StringComparison.CurrentCultureIgnoreCase)) + targetServer = null; + } + else + targetServer = null; + v2TaskService.Connect(targetServer, userName, userDomain, userPassword); + targetServer = v2TaskService.TargetServer; + userName = v2TaskService.ConnectedUser; + userDomain = v2TaskService.ConnectedDomain; + maxVer = GetV2Version(); + } + else + { + v1Impersonation = new WindowsImpersonatedIdentity(userName, userDomain, userPassword); + v1TaskScheduler = new ITaskScheduler(); + if (!string.IsNullOrEmpty(targetServer)) + { + // Check to ensure UNC format for server name. (Suggested by bigsan) + if (!targetServer.StartsWith(@"\\")) + targetServer = @"\\" + targetServer; + } + else + targetServer = null; + v1TaskScheduler.SetTargetComputer(targetServer); + targetServer = v1TaskScheduler.GetTargetComputer(); + maxVer = TaskServiceVersion.V1_1; + } + ServiceConnected?.Invoke(this, EventArgs.Empty); + connecting = false; + } + else + { + throw new ArgumentException("A username, password, and domain must be provided."); + } + } + } + + /// Finds the task in folder. + /// The folder. + /// The wildcard expression to compare task names with. + /// The results. + /// if set to true recurse folders. + /// True if any tasks are found, False if not. + private bool FindTaskInFolder([NotNull] TaskFolder fld, System.Text.RegularExpressions.Regex taskName, ref System.Collections.Generic.List results, bool recurse = true) + { + results.AddRange(fld.GetTasks(taskName)); + + if (recurse) + { + foreach (var f in fld.SubFolders) + { + if (FindTaskInFolder(f, taskName, ref results)) + return true; + } + } + return false; + } + + /// Finds the task in folder. + /// The folder. + /// The filter to use when looking for tasks. + /// The results. + /// if set to true recurse folders. + /// True if any tasks are found, False if not. + private bool FindTaskInFolder([NotNull] TaskFolder fld, Predicate filter, ref System.Collections.Generic.List results, bool recurse = true) + { + foreach (var t in fld.GetTasks()) + try + { + if (filter(t)) + results.Add(t); + } + catch + { + System.Diagnostics.Debug.WriteLine($"Unable to evaluate filter for task '{t.Path}'."); + } + + if (recurse) + { + foreach (var f in fld.SubFolders) + { + if (FindTaskInFolder(f, filter, ref results)) + return true; + } + } + return false; + } + + private Version GetV2Version() + { + var v = v2TaskService.HighestVersion; + return new Version((int)(v >> 16), (int)(v & 0x0000FFFF)); + } + + private void ResetHighestSupportedVersion() => maxVer = Connected ? (v2TaskService != null ? GetV2Version() : TaskServiceVersion.V1_1) : GetLibraryVersionFromLocalOS(); + + private void ResetUnsetProperties() + { + if (!maxVerSet) ResetHighestSupportedVersion(); + if (!targetServerSet) targetServer = null; + if (!userDomainSet) userDomain = null; + if (!userNameSet) userName = null; + if (!userPasswordSet) userPassword = null; + } + + private bool ShouldSerializeHighestSupportedVersion() => LibraryIsV2 && maxVer <= TaskServiceVersion.V1_1; + + private bool ShouldSerializeTargetServer() => targetServer != null && !targetServer.Trim('\\').Equals(Environment.MachineName.Trim('\\'), StringComparison.InvariantCultureIgnoreCase); + + private bool ShouldSerializeUserAccountDomain() => userDomain != null && !userDomain.Equals(Environment.UserDomainName, StringComparison.InvariantCultureIgnoreCase); + + private bool ShouldSerializeUserName() => userName != null && !userName.Equals(Environment.UserName, StringComparison.InvariantCultureIgnoreCase); + + /// + /// Represents a valid, connected session to a Task Scheduler instance. This token is thread-safe and should be the means of passing + /// information about a between threads. + /// + public struct ConnectionToken + { + internal int token; + + internal ConnectionToken(int value) => token = value; + } + + // Manages the list of tokens and associated data + private static class ConnectionDataManager + { + public static List connections = new List() { new ConnectionData(null) }; + + public static TaskService InstanceFromToken(ConnectionToken token) + { + ConnectionData data; + lock (connections) + { + data = connections[token.token < connections.Count ? token.token : 0]; + } + return new TaskService(data.TargetServer, data.UserName, data.UserAccountDomain, data.UserPassword, data.ForceV1); + } + + public static ConnectionToken TokenFromInstance(string targetServer, string userName = null, + string accountDomain = null, string password = null, bool forceV1 = false) + { + lock (connections) + { + var newData = new ConnectionData(targetServer, userName, accountDomain, password, forceV1); + for (var i = 0; i < connections.Count; i++) + { + if (connections[i].Equals(newData)) + return new ConnectionToken(i); + } + connections.Add(newData); + return new ConnectionToken(connections.Count - 1); + } + } + } + + private class ComHandlerThread + { + public int ReturnCode; + private readonly System.Threading.AutoResetEvent completed = new System.Threading.AutoResetEvent(false); + private readonly string Data; + private readonly Type objType; + private readonly TaskHandlerStatus status; + private readonly int Timeout; + + public ComHandlerThread(Guid clsid, string data, int millisecondsTimeout, ComHandlerUpdate onUpdate, Action onComplete) + { + objType = Type.GetTypeFromCLSID(clsid, true); + Data = data; + Timeout = millisecondsTimeout; + status = new TaskHandlerStatus(i => + { + completed.Set(); + onComplete?.Invoke(i); + }, onUpdate); + } + + public System.Threading.Thread Start() + { + var t = new System.Threading.Thread(ThreadProc); + t.Start(); + return t; + } + + private void ThreadProc() + { + completed.Reset(); + object obj = null; + try { obj = Activator.CreateInstance(objType); } catch { } + if (obj == null) return; + ITaskHandler taskHandler = null; + try { taskHandler = (ITaskHandler)obj; } catch { } + try + { + if (taskHandler != null) + { + taskHandler.Start(status, Data); + completed.WaitOne(Timeout); + taskHandler.Stop(out ReturnCode); + } + } + finally + { + if (taskHandler != null) + Marshal.ReleaseComObject(taskHandler); + Marshal.ReleaseComObject(obj); + } + } + + private class TaskHandlerStatus : ITaskHandlerStatus + { + private readonly Action OnCompleted; + private readonly ComHandlerUpdate OnUpdate; + + public TaskHandlerStatus(Action onCompleted, ComHandlerUpdate onUpdate) + { + OnCompleted = onCompleted; + OnUpdate = onUpdate; + } + + public void TaskCompleted([In, MarshalAs(UnmanagedType.Error)] int taskErrCode) => OnCompleted?.Invoke(taskErrCode); + + public void UpdateStatus([In] short percentComplete, [In, MarshalAs(UnmanagedType.BStr)] string statusMessage) => OnUpdate?.Invoke(percentComplete, statusMessage); + } + } + + // This private class holds information needed to create a new TaskService instance + private class ConnectionData : IEquatable + { + public bool ForceV1; + public string TargetServer, UserAccountDomain, UserName, UserPassword; + + public ConnectionData(string targetServer, string userName = null, string accountDomain = null, string password = null, bool forceV1 = false) + { + TargetServer = targetServer; + UserAccountDomain = accountDomain; + UserName = userName; + UserPassword = password; + ForceV1 = forceV1; + } + + public bool Equals(ConnectionData other) => string.Equals(TargetServer, other.TargetServer, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(UserAccountDomain, other.UserAccountDomain, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(UserName, other.UserName, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(UserPassword, other.UserPassword, StringComparison.InvariantCultureIgnoreCase) && + ForceV1 == other.ForceV1; + } + + private class VersionConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + return true; + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + var s = value as string; + return s != null ? new Version(s) : base.ConvertFrom(context, culture, value); + } + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Trigger.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Trigger.cs index 50753c671..e449858e1 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Trigger.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Trigger.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; using System.Xml.Serialization; using winPEAS.Properties; using winPEAS.TaskScheduler.V1; @@ -14,2769 +12,2769 @@ namespace winPEAS.TaskScheduler { - /// Values for days of the week (Monday, Tuesday, etc.) - [Flags] - public enum DaysOfTheWeek : short - { - /// Sunday - Sunday = 0x1, + /// Values for days of the week (Monday, Tuesday, etc.) + [Flags] + public enum DaysOfTheWeek : short + { + /// Sunday + Sunday = 0x1, - /// Monday - Monday = 0x2, + /// Monday + Monday = 0x2, - /// Tuesday - Tuesday = 0x4, + /// Tuesday + Tuesday = 0x4, - /// Wednesday - Wednesday = 0x8, + /// Wednesday + Wednesday = 0x8, - /// Thursday - Thursday = 0x10, + /// Thursday + Thursday = 0x10, - /// Friday - Friday = 0x20, + /// Friday + Friday = 0x20, - /// Saturday - Saturday = 0x40, + /// Saturday + Saturday = 0x40, - /// All days - AllDays = 0x7F - } + /// All days + AllDays = 0x7F + } - /// Values for months of the year (January, February, etc.) - [Flags] - public enum MonthsOfTheYear : short - { - /// January - January = 0x1, + /// Values for months of the year (January, February, etc.) + [Flags] + public enum MonthsOfTheYear : short + { + /// January + January = 0x1, - /// February - February = 0x2, + /// February + February = 0x2, - /// March - March = 0x4, + /// March + March = 0x4, - /// April - April = 0x8, + /// April + April = 0x8, - /// May - May = 0x10, + /// May + May = 0x10, - /// June - June = 0x20, + /// June + June = 0x20, - /// July - July = 0x40, + /// July + July = 0x40, - /// August - August = 0x80, + /// August + August = 0x80, - /// September - September = 0x100, + /// September + September = 0x100, - /// October - October = 0x200, + /// October + October = 0x200, - /// November - November = 0x400, + /// November + November = 0x400, - /// December - December = 0x800, + /// December + December = 0x800, - /// All months - AllMonths = 0xFFF - } - - /// Defines the type of triggers that can be used by tasks. - [DefaultValue(Time)] - public enum TaskTriggerType - { - /// Triggers the task when a specific event occurs. Version 1.2 only. - Event = 0, - - /// Triggers the task at a specific time of day. - Time = 1, + /// All months + AllMonths = 0xFFF + } + + /// Defines the type of triggers that can be used by tasks. + [DefaultValue(Time)] + public enum TaskTriggerType + { + /// Triggers the task when a specific event occurs. Version 1.2 only. + Event = 0, + + /// Triggers the task at a specific time of day. + Time = 1, - /// Triggers the task on a daily schedule. - Daily = 2, - - /// Triggers the task on a weekly schedule. - Weekly = 3, - - /// Triggers the task on a monthly schedule. - Monthly = 4, - - /// Triggers the task on a monthly day-of-week schedule. - MonthlyDOW = 5, - - /// Triggers the task when the computer goes into an idle state. - Idle = 6, - - /// Triggers the task when the task is registered. Version 1.2 only. - Registration = 7, - - /// Triggers the task when the computer boots. - Boot = 8, - - /// Triggers the task when a specific user logs on. - Logon = 9, - - /// Triggers the task when a specific user session state changes. Version 1.2 only. - SessionStateChange = 11, - - /// Triggers the custom trigger. Version 1.3 only. - Custom = 12 - } - - /// Values for week of month (first, second, ..., last) - [Flags] - public enum WhichWeek : short - { - /// First week of the month - FirstWeek = 1, - - /// Second week of the month - SecondWeek = 2, - - /// Third week of the month - ThirdWeek = 4, - - /// Fourth week of the month - FourthWeek = 8, - - /// Last week of the month - LastWeek = 0x10, - - /// Every week of the month - AllWeeks = 0x1F - } - - /// Interface that categorizes the trigger as a calendar trigger. - public interface ICalendarTrigger { } - - /// Interface for triggers that support a delay. - public interface ITriggerDelay - { - /// Gets or sets a value that indicates the amount of time before the task is started. - /// The delay duration. - TimeSpan Delay { get; set; } - } - - /// Interface for triggers that support a user identifier. - public interface ITriggerUserId - { - /// Gets or sets the user for the . - string UserId { get; set; } - } - - /// Represents a trigger that starts a task when the system is booted. - /// - /// A BootTrigger will fire when the system starts. It can only be delayed. All triggers that support a delay implement the - /// ITriggerDelay interface. - /// - /// - /// - /// - /// - /// - public sealed class BootTrigger : Trigger, ITriggerDelay - { - /// Creates an unbound instance of a . - public BootTrigger() : base(TaskTriggerType.Boot) { } - - internal BootTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.OnSystemStart) - { - } - - internal BootTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan Delay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IBootTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IBootTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(Delay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() => Properties.Resources.TriggerBoot1; - } - - /// - /// Represents a custom trigger. This class is based on undocumented features and may change. This type of trigger is only - /// available for reading custom triggers. It cannot be used to create custom triggers. - /// - public sealed class CustomTrigger : Trigger, ITriggerDelay - { - private readonly NamedValueCollection nvc = new NamedValueCollection(); - private TimeSpan delay = TimeSpan.MinValue; - private string name = string.Empty; - - internal CustomTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets a value that indicates the amount of time between the trigger events and when the task is started. - /// This value cannot be set. - public TimeSpan Delay - { - get => delay; - set => throw new NotImplementedException(); - } - - /// Gets the name of the custom trigger type. - /// The name of the XML element representing this custom trigger. - public string Name => name; - - /// Gets the properties from the XML definition if possible. - [XmlArray, XmlArrayItem("Property")] - public NamedValueCollection Properties => nvc; - - /// Clones this instance. - /// This method will always throw an exception. - /// CustomTrigger cannot be cloned due to OS restrictions. - public override object Clone() => throw new InvalidOperationException("CustomTrigger cannot be cloned due to OS restrictions."); - - /// Updates custom properties from XML provided by definition. - /// The XML from the TaskDefinition. - internal void UpdateFromXml(string xml) - { - nvc.Clear(); - try - { - var xmlDoc = new System.Xml.XmlDocument(); - xmlDoc.LoadXml(xml); - var nsmgr = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable); - nsmgr.AddNamespace("n", "http://schemas.microsoft.com/windows/2004/02/mit/task"); - var elem = xmlDoc.DocumentElement?.SelectSingleNode("n:Triggers/*[@id='" + Id + "']", nsmgr); - if (elem == null) - { - var nodes = xmlDoc.GetElementsByTagName("WnfStateChangeTrigger"); - if (nodes.Count == 1) - elem = nodes[0]; - } - - if (elem == null) return; - - name = elem.LocalName; - foreach (System.Xml.XmlNode node in elem.ChildNodes) - { - switch (node.LocalName) - { - case "Delay": - delay = Task.StringToTimeSpan(node.InnerText); - break; - - case "StartBoundary": - case "Enabled": - case "EndBoundary": - case "ExecutionTimeLimit": - break; - - default: - nvc.Add(node.LocalName, node.InnerText); - break; - } - } - } - catch { /* ignored */ } - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() => Resources.TriggerCustom1; - } - - /// - /// Represents a trigger that starts a task based on a daily schedule. For example, the task starts at a specific time every day, every - /// other day, every third day, and so on. - /// - /// A DailyTrigger will fire at a specified time every day or interval of days. - /// - /// - /// - /// - /// - [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class DailyTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable - { - /// Creates an unbound instance of a . - /// Interval between the days in the schedule. - public DailyTrigger(short daysInterval = 1) : base(TaskTriggerType.Daily) => DaysInterval = daysInterval; - - internal DailyTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunDaily) - { - if (v1TriggerData.Data.daily.DaysInterval == 0) - v1TriggerData.Data.daily.DaysInterval = 1; - } - - internal DailyTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Sets or retrieves the interval between the days in the schedule. - [DefaultValue(1)] - public short DaysInterval - { - get - { - if (v2Trigger != null) - return ((IDailyTrigger)v2Trigger).DaysInterval; - return (short)v1TriggerData.Data.daily.DaysInterval; - } - set - { - if (v2Trigger != null) - ((IDailyTrigger)v2Trigger).DaysInterval = value; - else - { - v1TriggerData.Data.daily.DaysInterval = (ushort)value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(DaysInterval)] = value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a delay time that is randomly added to the start time of the trigger. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan RandomDelay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IDailyTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IDailyTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RandomDelay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a value that indicates the amount of time before the task is started. - /// The delay duration. - TimeSpan ITriggerDelay.Delay - { - get => RandomDelay; - set => RandomDelay = value; - } - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public override void CopyProperties(Trigger sourceTrigger) - { - base.CopyProperties(sourceTrigger); - if (sourceTrigger is DailyTrigger dt) - { - DaysInterval = dt.DaysInterval; - } - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Trigger other) => other is DailyTrigger dt && base.Equals(dt) && DaysInterval == dt.DaysInterval; - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() => DaysInterval == 1 ? - string.Format(Properties.Resources.TriggerDaily1, AdjustToLocal(StartBoundary)) : - string.Format(Properties.Resources.TriggerDaily2, AdjustToLocal(StartBoundary), DaysInterval); - - private void ReadMyXml(System.Xml.XmlReader reader) - { - reader.ReadStartElement("ScheduleByDay"); - if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "DaysInterval") - // ReSharper disable once AssignNullToNotNullAttribute - DaysInterval = (short)reader.ReadElementContentAs(typeof(short), null); - reader.Read(); - reader.ReadEndElement(); - } - - private void WriteMyXml(System.Xml.XmlWriter writer) - { - writer.WriteStartElement("ScheduleByDay"); - writer.WriteElementString("DaysInterval", DaysInterval.ToString()); - writer.WriteEndElement(); - } - } - - /// - /// Represents a trigger that starts a task when a system event occurs. Only available for Task Scheduler 2.0 on Windows Vista or - /// Windows Server 2003 and later. - /// - /// The EventTrigger runs when a system event fires. - /// - /// - ///"; - /// eTrigger.ValueQueries.Add("Name", "Value"); - ///]]> - /// - /// - [XmlType(IncludeInSchema = false)] - public sealed class EventTrigger : Trigger, ITriggerDelay - { - private NamedValueCollection nvc; - - /// Creates an unbound instance of a . - public EventTrigger() : base(TaskTriggerType.Event) { } - - /// Initializes an unbound instance of the class and sets a basic event. - /// The event's log. - /// The event's source. Can be null. - /// The event's id. Can be null. - public EventTrigger(string log, string source, int? eventId) : this() => SetBasic(log, source, eventId); - - internal EventTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - public TimeSpan Delay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IEventTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IEventTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); - else - unboundValues[nameof(Delay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the XPath query string that identifies the event that fires the trigger. - [DefaultValue(null)] - public string Subscription - { - get => v2Trigger != null ? ((IEventTrigger)v2Trigger).Subscription : GetUnboundValueOrDefault(nameof(Subscription)); - set - { - if (v2Trigger != null) - ((IEventTrigger)v2Trigger).Subscription = value; - else - unboundValues[nameof(Subscription)] = value; - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets a collection of named XPath queries. Each query in the collection is applied to the last matching event XML returned from - /// the subscription query specified in the Subscription property. The name of the query can be used as a variable in the message of - /// a action. - /// - [XmlArray] - [XmlArrayItem("Value", typeof(NameValuePair))] - public NamedValueCollection ValueQueries => nvc ??= v2Trigger == null ? new NamedValueCollection() : new NamedValueCollection(((IEventTrigger)v2Trigger).ValueQueries); - - /// Builds an event log XML query string based on the input parameters. - /// The event's log. - /// The event's source. Can be null. - /// The event's id. Can be null. - /// XML query string. - /// log - public static string BuildQuery(string log, string source, int? eventId) - { - var sb = new StringBuilder(); - if (string.IsNullOrEmpty(log)) - throw new ArgumentNullException(nameof(log)); - sb.AppendFormat(""); - return sb.ToString(); - } - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public override void CopyProperties(Trigger sourceTrigger) - { - base.CopyProperties(sourceTrigger); - if (sourceTrigger is EventTrigger et) - { - Subscription = et.Subscription; - et.ValueQueries.CopyTo(ValueQueries); - } - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Trigger other) => other is EventTrigger et && base.Equals(et) && Subscription == et.Subscription; - - /// Gets basic event information. - /// The event's log. - /// The event's source. Can be null. - /// The event's id. Can be null. - /// true if subscription represents a basic event, false if not. - public bool GetBasic(out string log, out string source, out int? eventId) - { - log = source = null; - eventId = null; - if (!string.IsNullOrEmpty(Subscription)) - { - using var str = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(Subscription)); - using var rdr = new System.Xml.XmlTextReader(str) - { - WhitespaceHandling = System.Xml.WhitespaceHandling.None - }; - try - { - rdr.MoveToContent(); - rdr.ReadStartElement("QueryList"); - if (rdr.Name == "Query" && rdr.MoveToAttribute("Path")) - { - var path = rdr.Value; - if (rdr.MoveToElement() && rdr.ReadToDescendant("Select") && path.Equals(rdr["Path"], StringComparison.InvariantCultureIgnoreCase)) - { - var content = rdr.ReadString(); - var m = System.Text.RegularExpressions.Regex.Match(content, - @"\*(?:\[System\[(?:Provider\[\@Name='(?[^']+)'\])?(?:\s+and\s+)?(?:EventID=(?\d+))?\]\])", - System.Text.RegularExpressions.RegexOptions.IgnoreCase | - System.Text.RegularExpressions.RegexOptions.Compiled | - System.Text.RegularExpressions.RegexOptions.Singleline | - System.Text.RegularExpressions.RegexOptions.IgnorePatternWhitespace); - if (m.Success) - { - log = path; - if (m.Groups["s"].Success) - source = m.Groups["s"].Value; - if (m.Groups["e"].Success) - eventId = Convert.ToInt32(m.Groups["e"].Value); - return true; - } - } - } - } - catch { /* ignored */ } - } - return false; - } - - /// - /// Sets the subscription for a basic event. This will replace the contents of the property and clear all - /// entries in the property. - /// - /// The event's log. - /// The event's source. Can be null. - /// The event's id. Can be null. - public void SetBasic([NotNull] string log, string source, int? eventId) - { - ValueQueries.Clear(); - Subscription = BuildQuery(log, source, eventId); - } - - internal override void Bind(ITaskDefinition iTaskDef) - { - base.Bind(iTaskDef); - nvc?.Bind(((IEventTrigger)v2Trigger).ValueQueries); - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() - { - if (!GetBasic(out var log, out var source, out var id)) - return Properties.Resources.TriggerEvent1; - var sb = new StringBuilder(); - sb.AppendFormat(Properties.Resources.TriggerEventBasic1, log); - if (!string.IsNullOrEmpty(source)) - sb.AppendFormat(Properties.Resources.TriggerEventBasic2, source); - if (id.HasValue) - sb.AppendFormat(Properties.Resources.TriggerEventBasic3, id.Value); - return sb.ToString(); - } - } - - /// - /// Represents a trigger that starts a task when the computer goes into an idle state. For information about idle conditions, see Task - /// Idle Conditions. - /// - /// - /// An IdleTrigger will fire when the system becomes idle. It is generally a good practice to set a limit on how long it can run using - /// the ExecutionTimeLimit property. - /// - /// - /// - /// - /// - /// - public sealed class IdleTrigger : Trigger - { - /// Creates an unbound instance of a . - public IdleTrigger() : base(TaskTriggerType.Idle) { } - - internal IdleTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.OnIdle) - { - } - - internal IdleTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() => Properties.Resources.TriggerIdle1; - } - - /// - /// Represents a trigger that starts a task when a user logs on. When the Task Scheduler service starts, all logged-on users are - /// enumerated and any tasks registered with logon triggers that match the logged on user are run. Not available on Task Scheduler 1.0. - /// - /// - /// A LogonTrigger will fire after a user logs on. It can only be delayed. Under V2, you can specify which user it applies to. - /// - /// - /// - /// - /// - /// - public sealed class LogonTrigger : Trigger, ITriggerDelay, ITriggerUserId - { - /// Creates an unbound instance of a . - public LogonTrigger() : base(TaskTriggerType.Logon) { } - - internal LogonTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.OnLogon) - { - } - - internal LogonTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan Delay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((ILogonTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((ILogonTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(Delay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets The identifier of the user. For example, "MyDomain\MyName" or for a local account, "Administrator". - /// This property can be in one of the following formats: - /// • User name or SID: The task is started when the user logs on to the computer. - /// • NULL: The task is started when any user logs on to the computer. - /// - /// - /// If you want a task to be triggered when any member of a group logs on to the computer rather than when a specific user logs on, - /// then do not assign a value to the LogonTrigger.UserId property. Instead, create a logon trigger with an empty - /// LogonTrigger.UserId property and assign a value to the principal for the task using the Principal.GroupId property. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - [XmlIgnore] - public string UserId - { - get => v2Trigger != null ? ((ILogonTrigger)v2Trigger).UserId : GetUnboundValueOrDefault(nameof(UserId)); - set - { - if (v2Trigger != null) - ((ILogonTrigger)v2Trigger).UserId = value; - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(UserId)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() - { - var user = string.IsNullOrEmpty(UserId) ? Properties.Resources.TriggerAnyUser : UserId; - return string.Format(Properties.Resources.TriggerLogon1, user); - } - } - - /// - /// Represents a trigger that starts a task on a monthly day-of-week schedule. For example, the task starts on every first Thursday, May - /// through October. - /// - [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class MonthlyDOWTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable - { - /// Creates an unbound instance of a . - /// The days of the week. - /// The months of the year. - /// The weeks of the month. - public MonthlyDOWTrigger(DaysOfTheWeek daysOfWeek = DaysOfTheWeek.Sunday, MonthsOfTheYear monthsOfYear = MonthsOfTheYear.AllMonths, WhichWeek weeksOfMonth = WhichWeek.FirstWeek) : base(TaskTriggerType.MonthlyDOW) - { - DaysOfWeek = daysOfWeek; - MonthsOfYear = monthsOfYear; - WeeksOfMonth = weeksOfMonth; - } - - internal MonthlyDOWTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunMonthlyDOW) - { - if (v1TriggerData.Data.monthlyDOW.Months == 0) - v1TriggerData.Data.monthlyDOW.Months = MonthsOfTheYear.AllMonths; - if (v1TriggerData.Data.monthlyDOW.DaysOfTheWeek == 0) - v1TriggerData.Data.monthlyDOW.DaysOfTheWeek = DaysOfTheWeek.Sunday; - } - - internal MonthlyDOWTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets the days of the week during which the task runs. - [DefaultValue(0)] - public DaysOfTheWeek DaysOfWeek - { - get => v2Trigger != null - ? (DaysOfTheWeek)((IMonthlyDOWTrigger)v2Trigger).DaysOfWeek - : v1TriggerData.Data.monthlyDOW.DaysOfTheWeek; - set - { - if (v2Trigger != null) - ((IMonthlyDOWTrigger)v2Trigger).DaysOfWeek = (short)value; - else - { - v1TriggerData.Data.monthlyDOW.DaysOfTheWeek = value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(DaysOfWeek)] = (short)value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the months of the year during which the task runs. - [DefaultValue(0)] - public MonthsOfTheYear MonthsOfYear - { - get => v2Trigger != null - ? (MonthsOfTheYear)((IMonthlyDOWTrigger)v2Trigger).MonthsOfYear - : v1TriggerData.Data.monthlyDOW.Months; - set - { - if (v2Trigger != null) - ((IMonthlyDOWTrigger)v2Trigger).MonthsOfYear = (short)value; - else - { - v1TriggerData.Data.monthlyDOW.Months = value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(MonthsOfYear)] = (short)value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a delay time that is randomly added to the start time of the trigger. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan RandomDelay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IMonthlyDOWTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IMonthlyDOWTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RandomDelay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the task runs on the last week of the month. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(false)] - [XmlIgnore] - public bool RunOnLastWeekOfMonth - { - get => ((IMonthlyDOWTrigger)v2Trigger)?.RunOnLastWeekOfMonth ?? GetUnboundValueOrDefault(nameof(RunOnLastWeekOfMonth), false); - set - { - if (v2Trigger != null) - ((IMonthlyDOWTrigger)v2Trigger).RunOnLastWeekOfMonth = value; - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RunOnLastWeekOfMonth)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the weeks of the month during which the task runs. - [DefaultValue(0)] - public WhichWeek WeeksOfMonth - { - get - { - if (v2Trigger == null) - return v1Trigger != null - ? v1TriggerData.Data.monthlyDOW.V2WhichWeek - : GetUnboundValueOrDefault(nameof(WeeksOfMonth), WhichWeek.FirstWeek); - var ww = (WhichWeek)((IMonthlyDOWTrigger)v2Trigger).WeeksOfMonth; - // Following addition give accurate results for confusing RunOnLastWeekOfMonth property (thanks kbergeron) - if (((IMonthlyDOWTrigger)v2Trigger).RunOnLastWeekOfMonth) - ww |= WhichWeek.LastWeek; - return ww; - } - set - { - // In Windows 10, the native library no longer acknowledges the LastWeek value and requires the RunOnLastWeekOfMonth to be - // expressly set. I think this is wrong so I am correcting their changed functionality. (thanks @SebastiaanPolfliet) - if (value.IsFlagSet(WhichWeek.LastWeek)) - RunOnLastWeekOfMonth = true; - if (v2Trigger != null) - { - ((IMonthlyDOWTrigger)v2Trigger).WeeksOfMonth = (short)value; - } - else - { - try - { - v1TriggerData.Data.monthlyDOW.V2WhichWeek = value; - } - catch (NotV1SupportedException) - { - if (v1Trigger != null) throw; - } - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(WeeksOfMonth)] = (short)value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a value that indicates the amount of time before the task is started. - /// The delay duration. - TimeSpan ITriggerDelay.Delay - { - get => RandomDelay; - set => RandomDelay = value; - } - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public override void CopyProperties(Trigger sourceTrigger) - { - base.CopyProperties(sourceTrigger); - if (sourceTrigger is MonthlyDOWTrigger mt) - { - DaysOfWeek = mt.DaysOfWeek; - MonthsOfYear = mt.MonthsOfYear; - try { RunOnLastWeekOfMonth = mt.RunOnLastWeekOfMonth; } catch { /* ignored */ } - WeeksOfMonth = mt.WeeksOfMonth; - } - if (sourceTrigger is MonthlyTrigger m) - MonthsOfYear = m.MonthsOfYear; - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Trigger other) => other is MonthlyDOWTrigger mt && base.Equals(other) && DaysOfWeek == mt.DaysOfWeek && - MonthsOfYear == mt.MonthsOfYear && WeeksOfMonth == mt.WeeksOfMonth && v1Trigger == null && RunOnLastWeekOfMonth == mt.RunOnLastWeekOfMonth; - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() - { - var ww = TaskEnumGlobalizer.GetString(WeeksOfMonth); - var days = TaskEnumGlobalizer.GetString(DaysOfWeek); - var months = TaskEnumGlobalizer.GetString(MonthsOfYear); - return string.Format(Properties.Resources.TriggerMonthlyDOW1, AdjustToLocal(StartBoundary), ww, days, months); - } - - /// Reads the subclass XML for V1 streams. - /// The reader. - private void ReadMyXml([NotNull] System.Xml.XmlReader reader) - { - reader.ReadStartElement("ScheduleByMonthDayOfWeek"); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - switch (reader.LocalName) - { - case "Weeks": - reader.Read(); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - if (reader.LocalName == "Week") - { - var wk = reader.ReadElementContentAsString(); - if (wk == "Last") - WeeksOfMonth = WhichWeek.LastWeek; - else - { - WeeksOfMonth = (int.Parse(wk)) switch - { - 1 => WhichWeek.FirstWeek, - 2 => WhichWeek.SecondWeek, - 3 => WhichWeek.ThirdWeek, - 4 => WhichWeek.FourthWeek, - _ => throw new System.Xml.XmlException("Week element must contain a 1-4 or Last as content."), - }; - } - } - } - reader.ReadEndElement(); - break; - - case "DaysOfWeek": - reader.Read(); - DaysOfWeek = 0; - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - try - { - DaysOfWeek |= (DaysOfTheWeek)Enum.Parse(typeof(DaysOfTheWeek), reader.LocalName); - } - catch - { - throw new System.Xml.XmlException("Invalid days of the week element."); - } - reader.Read(); - } - reader.ReadEndElement(); - break; - - case "Months": - reader.Read(); - MonthsOfYear = 0; - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - try - { - MonthsOfYear |= (MonthsOfTheYear)Enum.Parse(typeof(MonthsOfTheYear), reader.LocalName); - } - catch - { - throw new System.Xml.XmlException("Invalid months of the year element."); - } - reader.Read(); - } - reader.ReadEndElement(); - break; - - default: - reader.Skip(); - break; - } - } - reader.ReadEndElement(); - } - - /// Writes the subclass XML for V1 streams. - /// The writer. - private void WriteMyXml([NotNull] System.Xml.XmlWriter writer) - { - writer.WriteStartElement("ScheduleByMonthDayOfWeek"); - - writer.WriteStartElement("Weeks"); - if ((WeeksOfMonth & WhichWeek.FirstWeek) == WhichWeek.FirstWeek) - writer.WriteElementString("Week", "1"); - if ((WeeksOfMonth & WhichWeek.SecondWeek) == WhichWeek.SecondWeek) - writer.WriteElementString("Week", "2"); - if ((WeeksOfMonth & WhichWeek.ThirdWeek) == WhichWeek.ThirdWeek) - writer.WriteElementString("Week", "3"); - if ((WeeksOfMonth & WhichWeek.FourthWeek) == WhichWeek.FourthWeek) - writer.WriteElementString("Week", "4"); - if ((WeeksOfMonth & WhichWeek.LastWeek) == WhichWeek.LastWeek) - writer.WriteElementString("Week", "Last"); - writer.WriteEndElement(); - - writer.WriteStartElement("DaysOfWeek"); - foreach (DaysOfTheWeek e in Enum.GetValues(typeof(DaysOfTheWeek))) - if (e != DaysOfTheWeek.AllDays && (DaysOfWeek & e) == e) - writer.WriteElementString(e.ToString(), null); - writer.WriteEndElement(); - - writer.WriteStartElement("Months"); - foreach (MonthsOfTheYear e in Enum.GetValues(typeof(MonthsOfTheYear))) - if (e != MonthsOfTheYear.AllMonths && (MonthsOfYear & e) == e) - writer.WriteElementString(e.ToString(), null); - writer.WriteEndElement(); - - writer.WriteEndElement(); - } - } - - /// - /// Represents a trigger that starts a job based on a monthly schedule. For example, the task starts on specific days of specific months. - /// - [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class MonthlyTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable - { - /// Creates an unbound instance of a . - /// - /// The day of the month. This must be a value between 1 and 32. If this value is set to 32, then the value will be set and no days will be added regardless of the month. - /// - /// The months of the year. - public MonthlyTrigger(int dayOfMonth = 1, MonthsOfTheYear monthsOfYear = MonthsOfTheYear.AllMonths) : base(TaskTriggerType.Monthly) - { - if (dayOfMonth < 1 || dayOfMonth > 32) throw new ArgumentOutOfRangeException(nameof(dayOfMonth)); - if (!monthsOfYear.IsValidFlagValue()) throw new ArgumentOutOfRangeException(nameof(monthsOfYear)); - if (dayOfMonth == 32) - { - DaysOfMonth = new int[0]; - RunOnLastDayOfMonth = true; - } - else - DaysOfMonth = new[] { dayOfMonth }; - MonthsOfYear = monthsOfYear; - } - - internal MonthlyTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunMonthly) - { - if (v1TriggerData.Data.monthlyDate.Months == 0) - v1TriggerData.Data.monthlyDate.Months = MonthsOfTheYear.AllMonths; - if (v1TriggerData.Data.monthlyDate.Days == 0) - v1TriggerData.Data.monthlyDate.Days = 1; - } - - internal MonthlyTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets the days of the month during which the task runs. - public int[] DaysOfMonth - { - get => v2Trigger != null ? MaskToIndices(((IMonthlyTrigger)v2Trigger).DaysOfMonth) : MaskToIndices((int)v1TriggerData.Data.monthlyDate.Days); - set - { - var mask = IndicesToMask(value); - if (v2Trigger != null) - ((IMonthlyTrigger)v2Trigger).DaysOfMonth = mask; - else - { - v1TriggerData.Data.monthlyDate.Days = (uint)mask; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(DaysOfMonth)] = mask; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the months of the year during which the task runs. - [DefaultValue(0)] - public MonthsOfTheYear MonthsOfYear - { - get => v2Trigger != null - ? (MonthsOfTheYear)((IMonthlyTrigger)v2Trigger).MonthsOfYear - : v1TriggerData.Data.monthlyDOW.Months; - set - { - if (v2Trigger != null) - ((IMonthlyTrigger)v2Trigger).MonthsOfYear = (short)value; - else - { - v1TriggerData.Data.monthlyDOW.Months = value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(MonthsOfYear)] = (short)value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a delay time that is randomly added to the start time of the trigger. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan RandomDelay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IMonthlyTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IMonthlyTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RandomDelay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a Boolean value that indicates that the task runs on the last day of the month. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(false)] - [XmlIgnore] - public bool RunOnLastDayOfMonth - { - get => ((IMonthlyTrigger)v2Trigger)?.RunOnLastDayOfMonth ?? GetUnboundValueOrDefault(nameof(RunOnLastDayOfMonth), false); - set - { - if (v2Trigger != null) - ((IMonthlyTrigger)v2Trigger).RunOnLastDayOfMonth = value; - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RunOnLastDayOfMonth)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a value that indicates the amount of time before the task is started. - /// The delay duration. - TimeSpan ITriggerDelay.Delay - { - get => RandomDelay; - set => RandomDelay = value; - } - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public override void CopyProperties(Trigger sourceTrigger) - { - base.CopyProperties(sourceTrigger); - if (sourceTrigger is MonthlyTrigger mt) - { - DaysOfMonth = mt.DaysOfMonth; - MonthsOfYear = mt.MonthsOfYear; - try { RunOnLastDayOfMonth = mt.RunOnLastDayOfMonth; } catch { /* ignored */ } - } - if (sourceTrigger is MonthlyDOWTrigger mdt) - MonthsOfYear = mdt.MonthsOfYear; - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Trigger other) => other is MonthlyTrigger mt && base.Equals(mt) && ListsEqual(DaysOfMonth, mt.DaysOfMonth) && - MonthsOfYear == mt.MonthsOfYear && v1Trigger == null && RunOnLastDayOfMonth == mt.RunOnLastDayOfMonth; - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() - { - var days = string.Join(Properties.Resources.ListSeparator, Array.ConvertAll(DaysOfMonth, i => i.ToString())); - if (RunOnLastDayOfMonth) - days += (days.Length == 0 ? "" : Properties.Resources.ListSeparator) + Properties.Resources.WWLastWeek; - var months = TaskEnumGlobalizer.GetString(MonthsOfYear); - return string.Format(Properties.Resources.TriggerMonthly1, AdjustToLocal(StartBoundary), days, months); - } - - /// - /// Converts an array of bit indices into a mask with bits turned ON at every index contained in the array. Indices must be from 1 - /// to 32 and bits are numbered the same. - /// - /// An array with an element for each bit of the mask which is ON. - /// An integer to be interpreted as a mask. - private static int IndicesToMask(int[] indices) - { - if (indices is null || indices.Length == 0) return 0; - var mask = 0; - foreach (var index in indices) - { - if (index < 1 || index > 31) throw new ArgumentException("Days must be in the range 1..31"); - mask |= 1 << (index - 1); - } - return mask; - } - - /// Compares two collections. - /// Item type of collections. - /// The first collection. - /// The second collection - /// true if the collections values are equal; false otherwise. - private static bool ListsEqual(ICollection left, ICollection right) where T : IComparable - { - if (left == null && right == null) return true; - if (left == null || right == null) return false; - if (left.Count != right.Count) return false; - List l1 = new List(left), l2 = new List(right); - l1.Sort(); l2.Sort(); - for (var i = 0; i < l1.Count; i++) - if (l1[i].CompareTo(l2[i]) != 0) return false; - return true; - } - - /// - /// Convert an integer representing a mask to an array where each element contains the index of a bit that is ON in the mask. Bits - /// are considered to number from 1 to 32. - /// - /// An integer to be interpreted as a mask. - /// An array with an element for each bit of the mask which is ON. - private static int[] MaskToIndices(int mask) - { - //count bits in mask - var cnt = 0; - for (var i = 0; mask >> i > 0; i++) - cnt += (1 & (mask >> i)); - //allocate return array with one entry for each bit - var indices = new int[cnt]; - //fill array with bit indices - cnt = 0; - for (var i = 0; mask >> i > 0; i++) - if ((1 & (mask >> i)) == 1) - indices[cnt++] = i + 1; - return indices; - } - - /// Reads the subclass XML for V1 streams. - /// The reader. - private void ReadMyXml([NotNull] System.Xml.XmlReader reader) - { - reader.ReadStartElement("ScheduleByMonth"); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - switch (reader.LocalName) - { - case "DaysOfMonth": - reader.Read(); - var days = new List(); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - if (reader.LocalName != "Day") continue; - var sday = reader.ReadElementContentAsString(); - if (sday.Equals("Last", StringComparison.InvariantCultureIgnoreCase)) continue; - var day = int.Parse(sday); - if (day >= 1 && day <= 31) - days.Add(day); - } - DaysOfMonth = days.ToArray(); - reader.ReadEndElement(); - break; - - case "Months": - reader.Read(); - MonthsOfYear = 0; - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - try - { - MonthsOfYear |= (MonthsOfTheYear)Enum.Parse(typeof(MonthsOfTheYear), reader.LocalName); - } - catch - { - throw new System.Xml.XmlException("Invalid months of the year element."); - } - reader.Read(); - } - reader.ReadEndElement(); - break; - - default: - reader.Skip(); - break; - } - } - reader.ReadEndElement(); - } - - private void WriteMyXml([NotNull] System.Xml.XmlWriter writer) - { - writer.WriteStartElement("ScheduleByMonth"); - - writer.WriteStartElement("DaysOfMonth"); - foreach (var day in DaysOfMonth) - writer.WriteElementString("Day", day.ToString()); - writer.WriteEndElement(); - - writer.WriteStartElement("Months"); - foreach (MonthsOfTheYear e in Enum.GetValues(typeof(MonthsOfTheYear))) - if (e != MonthsOfTheYear.AllMonths && (MonthsOfYear & e) == e) - writer.WriteElementString(e.ToString(), null); - writer.WriteEndElement(); - - writer.WriteEndElement(); - } - } - - /// - /// Represents a trigger that starts a task when the task is registered or updated. Not available on Task Scheduler 1.0. Only - /// available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and later. - /// - /// The RegistrationTrigger will fire after the task is registered (saved). It is advisable to put in a delay. - /// - /// - /// - /// - /// - [XmlType(IncludeInSchema = false)] - public sealed class RegistrationTrigger : Trigger, ITriggerDelay - { - /// Creates an unbound instance of a . - public RegistrationTrigger() : base(TaskTriggerType.Registration) { } - - internal RegistrationTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan Delay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IRegistrationTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IRegistrationTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(Delay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() => Properties.Resources.TriggerRegistration1; - } - - /// Defines how often the task is run and how long the repetition pattern is repeated after the task is started. - /// This can be used directly or by assignment for a . - /// - /// - /// - /// - /// - [XmlRoot("Repetition", Namespace = TaskDefinition.tns, IsNullable = true)] - [TypeConverter(typeof(RepetitionPatternConverter))] - public sealed class RepetitionPattern : IDisposable, IXmlSerializable, IEquatable, INotifyPropertyChanged - { - private readonly Trigger pTrigger; - private readonly IRepetitionPattern v2Pattern; - private TimeSpan unboundInterval = TimeSpan.Zero, unboundDuration = TimeSpan.Zero; - private bool unboundStopAtDurationEnd; - - /// Initializes a new instance of the class. - /// - /// The amount of time between each restart of the task. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. - /// - /// - /// The duration of how long the pattern is repeated. The minimum time allowed is one minute. If TimeSpan.Zero is specified, - /// the pattern is repeated indefinitely. - /// - /// - /// If set to true the running instance of the task is stopped at the end of repetition pattern duration. - /// - public RepetitionPattern(TimeSpan interval, TimeSpan duration, bool stopAtDurationEnd = false) - { - Interval = interval; - Duration = duration; - StopAtDurationEnd = stopAtDurationEnd; - } - - internal RepetitionPattern([NotNull] Trigger parent) - { - pTrigger = parent; - if (pTrigger?.v2Trigger != null) - v2Pattern = pTrigger.v2Trigger.Repetition; - } - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets or sets how long the pattern is repeated. - /// - /// The duration that the pattern is repeated. The minimum time allowed is one minute. If TimeSpan.Zero is specified, the - /// pattern is repeated indefinitely. - /// - /// If you specify a repetition duration for a task, you must also specify the repetition interval. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - public TimeSpan Duration - { - get => v2Pattern != null - ? Task.StringToTimeSpan(v2Pattern.Duration) - : (pTrigger != null ? TimeSpan.FromMinutes(pTrigger.v1TriggerData.MinutesDuration) : unboundDuration); - set - { - if (value.Ticks < 0 || value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) - throw new ArgumentOutOfRangeException(nameof(Duration)); - if (v2Pattern != null) - { - v2Pattern.Duration = Task.TimeSpanToString(value); - } - else if (pTrigger != null) - { - pTrigger.v1TriggerData.MinutesDuration = (uint)value.TotalMinutes; - Bind(); - } - else - unboundDuration = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the amount of time between each restart of the task. - /// - /// The amount of time between each restart of the task. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. - /// - /// If you specify a repetition duration for a task, you must also specify the repetition interval. - /// - /// The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. - /// - [DefaultValue(typeof(TimeSpan), "00:00:00")] - public TimeSpan Interval - { - get => v2Pattern != null - ? Task.StringToTimeSpan(v2Pattern.Interval) - : (pTrigger != null ? TimeSpan.FromMinutes(pTrigger.v1TriggerData.MinutesInterval) : unboundInterval); - set - { - if (value.Ticks < 0 || (v2Pattern != null || pTrigger == null) && value != TimeSpan.Zero && (value < TimeSpan.FromMinutes(1) || value > TimeSpan.FromDays(31))) - throw new ArgumentOutOfRangeException(nameof(Interval)); - if (v2Pattern != null) - { - v2Pattern.Interval = Task.TimeSpanToString(value); - } - else if (pTrigger != null) - { - if (value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) - throw new ArgumentOutOfRangeException(nameof(Interval)); - pTrigger.v1TriggerData.MinutesInterval = (uint)value.TotalMinutes; - Bind(); - } - else - unboundInterval = value; - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets a Boolean value that indicates if a running instance of the task is stopped at the end of repetition pattern duration. - /// - [DefaultValue(false)] - public bool StopAtDurationEnd - { - get - { - if (v2Pattern != null) - return v2Pattern.StopAtDurationEnd; - if (pTrigger != null) - return (pTrigger.v1TriggerData.Flags & TaskTriggerFlags.KillAtDurationEnd) == TaskTriggerFlags.KillAtDurationEnd; - return unboundStopAtDurationEnd; - } - set - { - if (v2Pattern != null) - v2Pattern.StopAtDurationEnd = value; - else if (pTrigger != null) - { - if (value) - pTrigger.v1TriggerData.Flags |= TaskTriggerFlags.KillAtDurationEnd; - else - pTrigger.v1TriggerData.Flags &= ~TaskTriggerFlags.KillAtDurationEnd; - Bind(); - } - else - unboundStopAtDurationEnd = value; - OnNotifyPropertyChanged(); - } - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Pattern != null) Marshal.ReleaseComObject(v2Pattern); - } - - /// Determines whether the specified , is equal to this instance. - /// The to compare with this instance. - /// true if the specified is equal to this instance; otherwise, false. - // ReSharper disable once BaseObjectEqualsIsObjectEquals - public override bool Equals(object obj) => obj is RepetitionPattern pattern ? Equals(pattern) : base.Equals(obj); - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public bool Equals(RepetitionPattern other) => other != null && Duration == other.Duration && Interval == other.Interval && StopAtDurationEnd == other.StopAtDurationEnd; - - /// Returns a hash code for this instance. - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() => new { A = Duration, B = Interval, C = StopAtDurationEnd }.GetHashCode(); - - /// Determines whether any properties for this have been set. - /// true if properties have been set; otherwise, false. - public bool IsSet() - { - if (v2Pattern != null) - return v2Pattern.StopAtDurationEnd || !string.IsNullOrEmpty(v2Pattern.Duration) || !string.IsNullOrEmpty(v2Pattern.Interval); - if (pTrigger != null) - return (pTrigger.v1TriggerData.Flags & TaskTriggerFlags.KillAtDurationEnd) == TaskTriggerFlags.KillAtDurationEnd || pTrigger.v1TriggerData.MinutesDuration > 0 || pTrigger.v1TriggerData.MinutesInterval > 0; - return false; - } - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) - { - if (!reader.IsEmptyElement) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); - XmlSerializationHelper.ReadObjectProperties(reader, this, ReadXmlConverter); - reader.ReadEndElement(); - } - else - reader.Skip(); - } - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => XmlSerializationHelper.WriteObjectProperties(writer, this); - - internal void Bind() - { - if (pTrigger.v1Trigger != null) - pTrigger.SetV1TriggerData(); - else if (pTrigger.v2Trigger != null) - { - if (pTrigger.v1TriggerData.MinutesInterval != 0) - v2Pattern.Interval = $"PT{pTrigger.v1TriggerData.MinutesInterval}M"; - if (pTrigger.v1TriggerData.MinutesDuration != 0) - v2Pattern.Duration = $"PT{pTrigger.v1TriggerData.MinutesDuration}M"; - v2Pattern.StopAtDurationEnd = (pTrigger.v1TriggerData.Flags & TaskTriggerFlags.KillAtDurationEnd) == TaskTriggerFlags.KillAtDurationEnd; - } - } - - internal void Set([NotNull] RepetitionPattern value) - { - Duration = value.Duration; - Interval = value.Interval; - StopAtDurationEnd = value.StopAtDurationEnd; - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - private bool ReadXmlConverter(System.Reflection.PropertyInfo pi, object obj, ref object value) - { - if (pi.Name != "Interval" || !(value is TimeSpan span) || span.Equals(TimeSpan.Zero) || Duration > span) - return false; - Duration = span.Add(TimeSpan.FromMinutes(1)); - return true; - } - } - - /// - /// Triggers tasks for console connect or disconnect, remote connect or disconnect, or workstation lock or unlock notifications. - /// Only available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and later. - /// - /// - /// The SessionStateChangeTrigger will fire after six different system events: connecting or disconnecting locally or remotely, or - /// locking or unlocking the session. - /// - /// - /// - /// - /// - /// - [XmlType(IncludeInSchema = false)] - public sealed class SessionStateChangeTrigger : Trigger, ITriggerDelay, ITriggerUserId - { - /// Creates an unbound instance of a . - public SessionStateChangeTrigger() : base(TaskTriggerType.SessionStateChange) { } - - /// Initializes a new instance of the class. - /// The state change. - /// The user identifier. - public SessionStateChangeTrigger(TaskSessionStateChangeType stateChange, string userId = null) : this() { StateChange = stateChange; UserId = userId; } - - internal SessionStateChangeTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - public TimeSpan Delay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((ISessionStateChangeTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((ISessionStateChangeTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); - else - unboundValues[nameof(Delay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the kind of Terminal Server session change that would trigger a task launch. - [DefaultValue(1)] - public TaskSessionStateChangeType StateChange - { - get => ((ISessionStateChangeTrigger)v2Trigger)?.StateChange ?? GetUnboundValueOrDefault(nameof(StateChange), TaskSessionStateChangeType.ConsoleConnect); - set - { - if (v2Trigger != null) - ((ISessionStateChangeTrigger)v2Trigger).StateChange = value; - else - unboundValues[nameof(StateChange)] = value; - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the user for the Terminal Server session. When a session state change is detected for this user, a task is started. - /// - [DefaultValue(null)] - public string UserId - { - get => v2Trigger != null ? ((ISessionStateChangeTrigger)v2Trigger).UserId : GetUnboundValueOrDefault(nameof(UserId)); - set - { - if (v2Trigger != null) - ((ISessionStateChangeTrigger)v2Trigger).UserId = value; - else - unboundValues[nameof(UserId)] = value; - OnNotifyPropertyChanged(); - } - } - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public override void CopyProperties(Trigger sourceTrigger) - { - base.CopyProperties(sourceTrigger); - if (sourceTrigger is SessionStateChangeTrigger st && !StateChangeIsSet()) - StateChange = st.StateChange; - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Trigger other) => other is SessionStateChangeTrigger st && base.Equals(st) && StateChange == st.StateChange; - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() - { - var str = Properties.Resources.ResourceManager.GetString("TriggerSession" + StateChange.ToString()); - var user = string.IsNullOrEmpty(UserId) ? Properties.Resources.TriggerAnyUser : UserId; - if (StateChange != TaskSessionStateChangeType.SessionLock && StateChange != TaskSessionStateChangeType.SessionUnlock) - user = string.Format(Properties.Resources.TriggerSessionUserSession, user); - return string.Format(str, user); - } - - /// Returns a value indicating if the StateChange property has been set. - /// StateChange property has been set. - private bool StateChangeIsSet() => v2Trigger != null || (unboundValues?.ContainsKey("StateChange") ?? false); - } - - /// Represents a trigger that starts a task at a specific date and time. - /// A TimeTrigger runs at a specified date and time. - /// - /// - /// - /// - /// - public sealed class TimeTrigger : Trigger, ITriggerDelay, ICalendarTrigger - { - /// Creates an unbound instance of a . - public TimeTrigger() : base(TaskTriggerType.Time) { } - - /// Creates an unbound instance of a and assigns the execution time. - /// Date and time for the trigger to fire. - public TimeTrigger(DateTime startBoundary) : base(TaskTriggerType.Time) => StartBoundary = startBoundary; - - internal TimeTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunOnce) - { - } - - internal TimeTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets a delay time that is randomly added to the start time of the trigger. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan RandomDelay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((ITimeTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((ITimeTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RandomDelay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a value that indicates the amount of time before the task is started. - /// The delay duration. - TimeSpan ITriggerDelay.Delay - { - get => RandomDelay; - set => RandomDelay = value; - } - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() => string.Format(Properties.Resources.TriggerTime1, AdjustToLocal(StartBoundary)); - } - - /// - /// Abstract base class which provides the common properties that are inherited by all trigger classes. A trigger can be created using - /// the or the method. - /// - public abstract partial class Trigger : IDisposable, ICloneable, IEquatable, IComparable, IComparable, INotifyPropertyChanged - { - internal const string V2BoundaryDateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFK"; - internal static readonly CultureInfo DefaultDateCulture = CultureInfo.CreateSpecificCulture("en-US"); - - internal ITaskTrigger v1Trigger; - internal TaskTrigger v1TriggerData; - internal ITrigger v2Trigger; - - /// In testing and may change. Do not use until officially introduced into library. - protected Dictionary unboundValues = new Dictionary(); - - private static bool? foundTimeSpan2; - private static Type timeSpan2Type; - private readonly TaskTriggerType ttype; - private RepetitionPattern repititionPattern; - - internal Trigger([NotNull] ITaskTrigger trigger, V1.TaskTriggerType type) - { - v1Trigger = trigger; - v1TriggerData = trigger.GetTrigger(); - v1TriggerData.Type = type; - ttype = ConvertFromV1TriggerType(type); - } - - internal Trigger([NotNull] ITrigger iTrigger) - { - v2Trigger = iTrigger; - ttype = iTrigger.Type; - if (string.IsNullOrEmpty(v2Trigger.StartBoundary) && this is ICalendarTrigger) - StartBoundary = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified); - } - - internal Trigger(TaskTriggerType triggerType) - { - ttype = triggerType; - - v1TriggerData.TriggerSize = (ushort)Marshal.SizeOf(typeof(TaskTrigger)); - if (ttype != TaskTriggerType.Registration && ttype != TaskTriggerType.Event && ttype != TaskTriggerType.SessionStateChange) - v1TriggerData.Type = ConvertToV1TriggerType(ttype); - - if (this is ICalendarTrigger) - StartBoundary = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified); - } - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets or sets a Boolean value that indicates whether the trigger is enabled. - public bool Enabled - { - get => v2Trigger?.Enabled ?? GetUnboundValueOrDefault(nameof(Enabled), !v1TriggerData.Flags.IsFlagSet(TaskTriggerFlags.Disabled)); - set - { - if (v2Trigger != null) - v2Trigger.Enabled = value; - else - { - v1TriggerData.Flags = v1TriggerData.Flags.SetFlags(TaskTriggerFlags.Disabled, !value); - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(Enabled)] = value; - } - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the date and time when the trigger is deactivated. The trigger cannot start the task after it is deactivated. - /// While the maximum value for this property is , the Windows Task Scheduler management - /// application that is part of the OS will fail if this value is greater than December 31, 9998. - /// - /// - /// - /// Version 1 (1.1 on all systems prior to Vista) of the native library only allows for the Day, Month and Year values of the structure. - /// - /// - /// Version 2 (1.2 or higher) of the native library only allows for both date and time and all values. - /// However, the user interface and methods will always show the time translated to local time. The - /// library makes every attempt to maintain the Kind value. When using the UI elements provided in the TaskSchedulerEditor library, - /// the "Synchronize across time zones" checkbox will be checked if the Kind is Local or Utc. If the Kind is Unspecified and the - /// user selects the checkbox, the Kind will be changed to Utc and the time adjusted from the value displayed as the local time. - /// - /// - [DefaultValue(typeof(DateTime), "9999-12-31T23:59:59.9999999")] - public DateTime EndBoundary - { - get - { - if (v2Trigger != null) - return string.IsNullOrEmpty(v2Trigger.EndBoundary) ? DateTime.MaxValue : DateTime.Parse(v2Trigger.EndBoundary, DefaultDateCulture); - return GetUnboundValueOrDefault(nameof(EndBoundary), v1TriggerData.EndDate.GetValueOrDefault(DateTime.MaxValue)); - } - set - { - if (v2Trigger != null) - { - if (value <= StartBoundary) - throw new ArgumentException(Properties.Resources.Error_TriggerEndBeforeStart); - v2Trigger.EndBoundary = value == DateTime.MaxValue ? null : value.ToString(V2BoundaryDateFormat, DefaultDateCulture); - } - else - { - v1TriggerData.EndDate = value == DateTime.MaxValue ? (DateTime?)null : value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(EndBoundary)] = value; - } - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets or sets the maximum amount of time that the task launched by this trigger is allowed to run. Not available with Task - /// Scheduler 1.0. - /// - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan ExecutionTimeLimit - { - get => v2Trigger != null ? Task.StringToTimeSpan(v2Trigger.ExecutionTimeLimit) : GetUnboundValueOrDefault(nameof(ExecutionTimeLimit), TimeSpan.Zero); - set - { - if (v2Trigger != null) - v2Trigger.ExecutionTimeLimit = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(ExecutionTimeLimit)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the identifier for the trigger. Cannot set with Task Scheduler 1.0. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(null)] - [XmlIgnore] - public string Id - { - get => v2Trigger != null ? v2Trigger.Id : GetUnboundValueOrDefault(nameof(Id)); - set - { - if (v2Trigger != null) - v2Trigger.Id = value; - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(Id)] = value; - OnNotifyPropertyChanged(); - } - } - - /// - /// Gets a instance that indicates how often the task is run and how long the repetition pattern is - /// repeated after the task is started. - /// - public RepetitionPattern Repetition - { - get => repititionPattern ??= new RepetitionPattern(this); - set - { - Repetition.Set(value); - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the date and time when the trigger is activated. - /// - /// - /// Version 1 (1.1 on all systems prior to Vista) of the native library only allows for values where the is unspecified. If the DateTime value Kind is then it will be used as - /// is. If the DateTime value Kind is then it will be converted to the local time and then used. - /// - /// - /// Version 2 (1.2 or higher) of the native library only allows for all values. However, the user - /// interface and methods will always show the time translated to local time. The library makes - /// every attempt to maintain the Kind value. When using the UI elements provided in the TaskSchedulerEditor library, the - /// "Synchronize across time zones" checkbox will be checked if the Kind is Local or Utc. If the Kind is Unspecified and the user - /// selects the checkbox, the Kind will be changed to Utc and the time adjusted from the value displayed as the local time. - /// - /// - /// Under Version 2, when converting the string used in the native library for this value (ITrigger.Startboundary) this library will - /// behave as follows: - /// - /// - /// YYYY-MM-DDTHH:MM:SS format uses DateTimeKind.Unspecified and the time specified. - /// - /// - /// YYYY-MM-DDTHH:MM:SSZ format uses DateTimeKind.Utc and the time specified as the GMT time. - /// - /// - /// YYYY-MM-DDTHH:MM:SS±HH:MM format uses DateTimeKind.Local and the time specified in that time zone. - /// - /// - /// - /// - public DateTime StartBoundary - { - get - { - if (v2Trigger == null) return GetUnboundValueOrDefault(nameof(StartBoundary), v1TriggerData.BeginDate); - if (string.IsNullOrEmpty(v2Trigger.StartBoundary)) - return DateTime.MinValue; - var ret = DateTime.Parse(v2Trigger.StartBoundary, DefaultDateCulture); - if (v2Trigger.StartBoundary.EndsWith("Z")) - ret = ret.ToUniversalTime(); - return ret; - } - set - { - if (v2Trigger != null) - { - if (value > EndBoundary) - throw new ArgumentException(Properties.Resources.Error_TriggerEndBeforeStart); - v2Trigger.StartBoundary = value == DateTime.MinValue ? null : value.ToString(V2BoundaryDateFormat, DefaultDateCulture); - } - else - { - v1TriggerData.BeginDate = value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(StartBoundary)] = value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets the type of the trigger. - /// The of the trigger. - [XmlIgnore] - public TaskTriggerType TriggerType => ttype; - - /// Creates the specified trigger. - /// Type of the trigger to instantiate. - /// of specified type. - public static Trigger CreateTrigger(TaskTriggerType triggerType) - { - switch (triggerType) - { - case TaskTriggerType.Boot: - return new BootTrigger(); - - case TaskTriggerType.Daily: - return new DailyTrigger(); - - case TaskTriggerType.Event: - return new EventTrigger(); - - case TaskTriggerType.Idle: - return new IdleTrigger(); - - case TaskTriggerType.Logon: - return new LogonTrigger(); - - case TaskTriggerType.Monthly: - return new MonthlyTrigger(); - - case TaskTriggerType.MonthlyDOW: - return new MonthlyDOWTrigger(); - - case TaskTriggerType.Registration: - return new RegistrationTrigger(); - - case TaskTriggerType.SessionStateChange: - return new SessionStateChangeTrigger(); - - case TaskTriggerType.Time: - return new TimeTrigger(); - - case TaskTriggerType.Weekly: - return new WeeklyTrigger(); - - case TaskTriggerType.Custom: - break; - - default: - throw new ArgumentOutOfRangeException(nameof(triggerType), triggerType, null); - } - return null; - } - - /// Creates a new that is an unbound copy of this instance. - /// A new that is an unbound copy of this instance. - public virtual object Clone() - { - var ret = CreateTrigger(TriggerType); - ret.CopyProperties(this); - return ret; - } - - /// - /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current - /// instance precedes, follows, or occurs in the same position in the sort order as the other object. - /// - /// An object to compare with this instance. - /// A value that indicates the relative order of the objects being compared. - public int CompareTo(Trigger other) => string.Compare(Id, other?.Id, StringComparison.InvariantCulture); - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public virtual void CopyProperties(Trigger sourceTrigger) - { - if (sourceTrigger == null) - return; - Enabled = sourceTrigger.Enabled; - EndBoundary = sourceTrigger.EndBoundary; - try { ExecutionTimeLimit = sourceTrigger.ExecutionTimeLimit; } - catch { /* ignored */ } - Id = sourceTrigger.Id; - Repetition.Duration = sourceTrigger.Repetition.Duration; - Repetition.Interval = sourceTrigger.Repetition.Interval; - Repetition.StopAtDurationEnd = sourceTrigger.Repetition.StopAtDurationEnd; - StartBoundary = sourceTrigger.StartBoundary; - if (sourceTrigger is ITriggerDelay delay && this is ITriggerDelay) - try { ((ITriggerDelay)this).Delay = delay.Delay; } - catch { /* ignored */ } - if (sourceTrigger is ITriggerUserId id && this is ITriggerUserId) - try { ((ITriggerUserId)this).UserId = id.UserId; } - catch { /* ignored */ } - } - - /// Releases all resources used by this class. - public virtual void Dispose() - { - if (v2Trigger != null) - Marshal.ReleaseComObject(v2Trigger); - if (v1Trigger != null) - Marshal.ReleaseComObject(v1Trigger); - } - - /// Determines whether the specified , is equal to this instance. - /// The to compare with this instance. - /// true if the specified is equal to this instance; otherwise, false. - // ReSharper disable once BaseObjectEqualsIsObjectEquals - public override bool Equals(object obj) => obj is Trigger trigger ? Equals(trigger) : base.Equals(obj); - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public virtual bool Equals(Trigger other) - { - if (other == null) return false; - var ret = TriggerType == other.TriggerType && Enabled == other.Enabled && EndBoundary == other.EndBoundary && - ExecutionTimeLimit == other.ExecutionTimeLimit && Id == other.Id && Repetition.Equals(other.Repetition) && - StartBoundary == other.StartBoundary; - if (other is ITriggerDelay delay && this is ITriggerDelay) - try { ret = ret && ((ITriggerDelay)this).Delay == delay.Delay; } - catch { /* ignored */ } - if (other is ITriggerUserId id && this is ITriggerUserId) - try { ret = ret && ((ITriggerUserId)this).UserId == id.UserId; } - catch { /* ignored */ } - return ret; - } - - /// Returns a hash code for this instance. - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() => new - { - A = TriggerType, - B = Enabled, - C = EndBoundary, - D = ExecutionTimeLimit, - E = Id, - F = Repetition, - G = StartBoundary, - H = (this as ITriggerDelay)?.Delay ?? TimeSpan.Zero, - I = (this as ITriggerUserId)?.UserId - }.GetHashCode(); - - /// Sets the repetition. - /// - /// The amount of time between each restart of the task. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. - /// - /// - /// The duration of how long the pattern is repeated. The minimum time allowed is one minute. If TimeSpan.Zero is specified, - /// the pattern is repeated indefinitely. - /// - /// - /// if set to true the running instance of the task is stopped at the end of repetition pattern duration. - /// - [Obsolete("Set the Repetition property directly with a new instance of RepetitionPattern.", false)] - public void SetRepetition(TimeSpan interval, TimeSpan duration, bool stopAtDurationEnd = true) - { - Repetition.Duration = duration; - Repetition.Interval = interval; - Repetition.StopAtDurationEnd = stopAtDurationEnd; - } - - /// Returns a string representing this trigger. - /// String value of trigger. - public override string ToString() => v1Trigger != null ? v1Trigger.GetTriggerString() : V2GetTriggerString() + V2BaseTriggerString(); - - /// Returns a that represents this trigger in a specific language. - /// The language of the resulting string. - /// String value of trigger. - public virtual string ToString([NotNull] CultureInfo culture) - { - using (new CultureSwitcher(culture)) - return ToString(); - } - - int IComparable.CompareTo(object obj) => CompareTo(obj as Trigger); - - internal static DateTime AdjustToLocal(DateTime dt) => dt.Kind == DateTimeKind.Utc ? dt.ToLocalTime() : dt; - - internal static V1.TaskTriggerType ConvertToV1TriggerType(TaskTriggerType type) - { - if (type == TaskTriggerType.Registration || type == TaskTriggerType.Event || type == TaskTriggerType.SessionStateChange) - throw new NotV1SupportedException(); - var tv1 = (int)type - 1; - if (tv1 >= 7) tv1--; - return (V1.TaskTriggerType)tv1; - } - - internal static Trigger CreateTrigger([NotNull] ITaskTrigger trigger) => CreateTrigger(trigger, trigger.GetTrigger().Type); - - internal static Trigger CreateTrigger([NotNull] ITaskTrigger trigger, V1.TaskTriggerType triggerType) - { - Trigger t = triggerType switch - { - V1.TaskTriggerType.RunOnce => new TimeTrigger(trigger), - V1.TaskTriggerType.RunDaily => new DailyTrigger(trigger), - V1.TaskTriggerType.RunWeekly => new WeeklyTrigger(trigger), - V1.TaskTriggerType.RunMonthly => new MonthlyTrigger(trigger), - V1.TaskTriggerType.RunMonthlyDOW => new MonthlyDOWTrigger(trigger), - V1.TaskTriggerType.OnIdle => new IdleTrigger(trigger), - V1.TaskTriggerType.OnSystemStart => new BootTrigger(trigger), - V1.TaskTriggerType.OnLogon => new LogonTrigger(trigger), - _ => throw new ArgumentOutOfRangeException(nameof(triggerType), triggerType, null), - }; - return t; - } - - internal static Trigger CreateTrigger([NotNull] ITrigger iTrigger, ITaskDefinition iDef = null) - { - switch (iTrigger.Type) - { - case TaskTriggerType.Boot: - return new BootTrigger((IBootTrigger)iTrigger); - - case TaskTriggerType.Daily: - return new DailyTrigger((IDailyTrigger)iTrigger); - - case TaskTriggerType.Event: - return new EventTrigger((IEventTrigger)iTrigger); - - case TaskTriggerType.Idle: - return new IdleTrigger((IIdleTrigger)iTrigger); - - case TaskTriggerType.Logon: - return new LogonTrigger((ILogonTrigger)iTrigger); - - case TaskTriggerType.Monthly: - return new MonthlyTrigger((IMonthlyTrigger)iTrigger); - - case TaskTriggerType.MonthlyDOW: - return new MonthlyDOWTrigger((IMonthlyDOWTrigger)iTrigger); - - case TaskTriggerType.Registration: - return new RegistrationTrigger((IRegistrationTrigger)iTrigger); - - case TaskTriggerType.SessionStateChange: - return new SessionStateChangeTrigger((ISessionStateChangeTrigger)iTrigger); - - case TaskTriggerType.Time: - return new TimeTrigger((ITimeTrigger)iTrigger); - - case TaskTriggerType.Weekly: - return new WeeklyTrigger((IWeeklyTrigger)iTrigger); - - case TaskTriggerType.Custom: - var ct = new CustomTrigger(iTrigger); - if (iDef != null) - try { ct.UpdateFromXml(iDef.XmlText); } catch { /* ignored */ } - return ct; - - default: - throw new ArgumentOutOfRangeException(); - } - } - - /// Gets the best time span string. - /// The to display. - /// Either the full string representation created by TimeSpan2 or the default TimeSpan representation. - internal static string GetBestTimeSpanString(TimeSpan span) - { - // See if the TimeSpan2 assembly is accessible - if (!foundTimeSpan2.HasValue) - { - try - { - foundTimeSpan2 = false; - timeSpan2Type = ReflectionHelper.LoadType("System.TimeSpan2", "TimeSpan2.dll"); - if (timeSpan2Type != null) - foundTimeSpan2 = true; - } - catch { /* ignored */ } - } - - // If the TimeSpan2 assembly is available, try to call the ToString("f") method and return the result. - if (foundTimeSpan2 == true && timeSpan2Type != null) - { - try - { - return ReflectionHelper.InvokeMethod(timeSpan2Type, new object[] { span }, "ToString", "f"); - } - catch { /* ignored */ } - } - - return span.ToString(); - } - - internal virtual void Bind([NotNull] ITask iTask) - { - if (v1Trigger == null) - { - v1Trigger = iTask.CreateTrigger(out var _); - } - SetV1TriggerData(); - } - - internal virtual void Bind([NotNull] ITaskDefinition iTaskDef) - { - var iTriggers = iTaskDef.Triggers; - v2Trigger = iTriggers.Create(ttype); - Marshal.ReleaseComObject(iTriggers); - if ((unboundValues.TryGetValue("StartBoundary", out var dt) ? (DateTime)dt : StartBoundary) > (unboundValues.TryGetValue("EndBoundary", out dt) ? (DateTime)dt : EndBoundary)) - throw new ArgumentException(Properties.Resources.Error_TriggerEndBeforeStart); - foreach (var key in unboundValues.Keys) - { - try - { - var o = unboundValues[key]; - CheckBindValue(key, ref o); - v2Trigger.GetType().InvokeMember(key, System.Reflection.BindingFlags.SetProperty, null, v2Trigger, new[] { o }); - } - catch (System.Reflection.TargetInvocationException tie) when (tie.InnerException != null) { throw tie.InnerException; } - catch { /* ignored */ } - } - unboundValues.Clear(); - unboundValues = null; - - repititionPattern = new RepetitionPattern(this); - repititionPattern.Bind(); - } - - /// Assigns the unbound TriggerData structure to the V1 trigger instance. - internal void SetV1TriggerData() - { - if (v1TriggerData.MinutesInterval != 0 && v1TriggerData.MinutesInterval >= v1TriggerData.MinutesDuration) - throw new ArgumentException("Trigger.Repetition.Interval must be less than Trigger.Repetition.Duration under Task Scheduler 1.0."); - if (v1TriggerData.EndDate <= v1TriggerData.BeginDate) - throw new ArgumentException(Properties.Resources.Error_TriggerEndBeforeStart); - if (v1TriggerData.BeginDate == DateTime.MinValue) - v1TriggerData.BeginDate = DateTime.Now; - v1Trigger?.SetTrigger(ref v1TriggerData); - System.Diagnostics.Debug.WriteLine(v1TriggerData); - } - - /// Checks the bind value for any conversion. - /// The key (property) name. - /// The value. - protected virtual void CheckBindValue(string key, ref object o) - { - if (o is TimeSpan ts) - o = Task.TimeSpanToString(ts); - if (o is DateTime dt) - { - if (key == "EndBoundary" && dt == DateTime.MaxValue || key == "StartBoundary" && dt == DateTime.MinValue) - o = null; - else - o = dt.ToString(V2BoundaryDateFormat, DefaultDateCulture); - } - } - - /// Gets the unbound value or a default. - /// Return type. - /// The property name. - /// The default value if not found in unbound value list. - /// The unbound value, if set, or the default value. - protected T GetUnboundValueOrDefault(string prop, T def = default) => unboundValues.TryGetValue(prop, out var val) ? (T)val : def; - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - protected void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected virtual string V2GetTriggerString() => string.Empty; - - private static TaskTriggerType ConvertFromV1TriggerType(V1.TaskTriggerType v1Type) - { - var tv2 = (int)v1Type + 1; - if (tv2 > 6) tv2++; - return (TaskTriggerType)tv2; - } - - private string V2BaseTriggerString() - { - var ret = new StringBuilder(); - if (Repetition.Interval != TimeSpan.Zero) - { - var sduration = Repetition.Duration == TimeSpan.Zero ? Properties.Resources.TriggerDuration0 : string.Format(Properties.Resources.TriggerDurationNot0, GetBestTimeSpanString(Repetition.Duration)); - ret.AppendFormat(Properties.Resources.TriggerRepetition, GetBestTimeSpanString(Repetition.Interval), sduration); - } - if (EndBoundary != DateTime.MaxValue) - ret.AppendFormat(Properties.Resources.TriggerEndBoundary, AdjustToLocal(EndBoundary)); - if (ret.Length > 0) - ret.Insert(0, Properties.Resources.HyphenSeparator); - return ret.ToString(); - } - } - - /// - /// Represents a trigger that starts a task based on a weekly schedule. For example, the task starts at 8:00 A.M. on a specific day of - /// the week every week or every other week. - /// - /// A WeeklyTrigger runs at a specified time on specified days of the week every week or interval of weeks. - /// - /// - /// - /// - /// - [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class WeeklyTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable - { - /// Creates an unbound instance of a . - /// The days of the week. - /// The interval between the weeks in the schedule. - public WeeklyTrigger(DaysOfTheWeek daysOfWeek = DaysOfTheWeek.Sunday, short weeksInterval = 1) : base(TaskTriggerType.Weekly) - { - DaysOfWeek = daysOfWeek; - WeeksInterval = weeksInterval; - } - - internal WeeklyTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunWeekly) - { - if (v1TriggerData.Data.weekly.DaysOfTheWeek == 0) - v1TriggerData.Data.weekly.DaysOfTheWeek = DaysOfTheWeek.Sunday; - if (v1TriggerData.Data.weekly.WeeksInterval == 0) - v1TriggerData.Data.weekly.WeeksInterval = 1; - } - - internal WeeklyTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) - { - } - - /// Gets or sets the days of the week on which the task runs. - [DefaultValue(0)] - public DaysOfTheWeek DaysOfWeek - { - get => v2Trigger != null - ? (DaysOfTheWeek)((IWeeklyTrigger)v2Trigger).DaysOfWeek - : v1TriggerData.Data.weekly.DaysOfTheWeek; - set - { - if (v2Trigger != null) - ((IWeeklyTrigger)v2Trigger).DaysOfWeek = (short)value; - else - { - v1TriggerData.Data.weekly.DaysOfTheWeek = value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(DaysOfWeek)] = (short)value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a delay time that is randomly added to the start time of the trigger. - /// Not supported under Task Scheduler 1.0. - [DefaultValue(typeof(TimeSpan), "00:00:00")] - [XmlIgnore] - public TimeSpan RandomDelay - { - get => v2Trigger != null ? Task.StringToTimeSpan(((IWeeklyTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); - set - { - if (v2Trigger != null) - ((IWeeklyTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); - else if (v1Trigger != null) - throw new NotV1SupportedException(); - else - unboundValues[nameof(RandomDelay)] = value; - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets the interval between the weeks in the schedule. - [DefaultValue(1)] - public short WeeksInterval - { - get => ((IWeeklyTrigger)v2Trigger)?.WeeksInterval ?? (short)v1TriggerData.Data.weekly.WeeksInterval; - set - { - if (v2Trigger != null) - ((IWeeklyTrigger)v2Trigger).WeeksInterval = value; - else - { - v1TriggerData.Data.weekly.WeeksInterval = (ushort)value; - if (v1Trigger != null) - SetV1TriggerData(); - else - unboundValues[nameof(WeeksInterval)] = value; - } - OnNotifyPropertyChanged(); - } - } - - /// Gets or sets a value that indicates the amount of time before the task is started. - /// The delay duration. - TimeSpan ITriggerDelay.Delay - { - get => RandomDelay; - set => RandomDelay = value; - } - - /// - /// Copies the properties from another the current instance. This will not copy any properties associated with - /// any derived triggers except those supporting the interface. - /// - /// The source . - public override void CopyProperties(Trigger sourceTrigger) - { - base.CopyProperties(sourceTrigger); - if (sourceTrigger is WeeklyTrigger wt) - { - DaysOfWeek = wt.DaysOfWeek; - WeeksInterval = wt.WeeksInterval; - } - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public override bool Equals(Trigger other) => other is WeeklyTrigger wt && base.Equals(wt) && DaysOfWeek == wt.DaysOfWeek && WeeksInterval == wt.WeeksInterval; - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); - - /// Gets the non-localized trigger string for V2 triggers. - /// String describing the trigger. - protected override string V2GetTriggerString() - { - var days = TaskEnumGlobalizer.GetString(DaysOfWeek); - return string.Format(WeeksInterval == 1 ? Properties.Resources.TriggerWeekly1Week : Properties.Resources.TriggerWeeklyMultWeeks, AdjustToLocal(StartBoundary), days, WeeksInterval); - } - - /// Reads the subclass XML for V1 streams. - /// The reader. - private void ReadMyXml(System.Xml.XmlReader reader) - { - reader.ReadStartElement("ScheduleByWeek"); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - switch (reader.LocalName) - { - case "WeeksInterval": - WeeksInterval = (short)reader.ReadElementContentAsInt(); - break; - - case "DaysOfWeek": - reader.Read(); - DaysOfWeek = 0; - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - try - { - DaysOfWeek |= (DaysOfTheWeek)Enum.Parse(typeof(DaysOfTheWeek), reader.LocalName); - } - catch - { - throw new System.Xml.XmlException("Invalid days of the week element."); - } - reader.Read(); - } - reader.ReadEndElement(); - break; - - default: - reader.Skip(); - break; - } - } - reader.ReadEndElement(); - } - - /// Writes the subclass XML for V1 streams. - /// The writer. - private void WriteMyXml(System.Xml.XmlWriter writer) - { - writer.WriteStartElement("ScheduleByWeek"); - - if (WeeksInterval != 1) - writer.WriteElementString("WeeksInterval", WeeksInterval.ToString()); - - writer.WriteStartElement("DaysOfWeek"); - foreach (DaysOfTheWeek e in Enum.GetValues(typeof(DaysOfTheWeek))) - if (e != DaysOfTheWeek.AllDays && (DaysOfWeek & e) == e) - writer.WriteElementString(e.ToString(), null); - writer.WriteEndElement(); - - writer.WriteEndElement(); - } - } - - internal static class CalendarTrigger - { - internal delegate void CalendarXmlReader(System.Xml.XmlReader reader); - - internal delegate void CalendarXmlWriter(System.Xml.XmlWriter writer); - - public static void WriteXml([NotNull] System.Xml.XmlWriter writer, [NotNull] Trigger t, [NotNull] CalendarXmlWriter calWriterProc) - { - if (!t.Enabled) - writer.WriteElementString("Enabled", System.Xml.XmlConvert.ToString(t.Enabled)); - if (t.EndBoundary != DateTime.MaxValue) - writer.WriteElementString("EndBoundary", System.Xml.XmlConvert.ToString(t.EndBoundary, System.Xml.XmlDateTimeSerializationMode.RoundtripKind)); - XmlSerializationHelper.WriteObject(writer, t.Repetition); - writer.WriteElementString("StartBoundary", System.Xml.XmlConvert.ToString(t.StartBoundary, System.Xml.XmlDateTimeSerializationMode.RoundtripKind)); - calWriterProc(writer); - } - - internal static Trigger GetTriggerFromXml([NotNull] System.Xml.XmlReader reader) - { - Trigger t = null; - var xml = reader.ReadOuterXml(); - var match = System.Text.RegularExpressions.Regex.Match(xml, @"\<(?ScheduleBy.+)\>"); - if (match.Success && match.Groups.Count == 2) - { - switch (match.Groups[1].Value) - { - case "ScheduleByDay": - t = new DailyTrigger(); - break; - - case "ScheduleByWeek": - t = new WeeklyTrigger(); - break; - - case "ScheduleByMonth": - t = new MonthlyTrigger(); - break; - - case "ScheduleByMonthDayOfWeek": - t = new MonthlyDOWTrigger(); - break; - } - - if (t != null) - { - using var ms = new System.IO.StringReader(xml); - using var iReader = System.Xml.XmlReader.Create(ms); - ((IXmlSerializable)t).ReadXml(iReader); - } - } - return t; - } - - internal static void ReadXml([NotNull] System.Xml.XmlReader reader, [NotNull] Trigger t, [NotNull] CalendarXmlReader calReaderProc) - { - reader.ReadStartElement("CalendarTrigger", TaskDefinition.tns); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - switch (reader.LocalName) - { - case "Enabled": - t.Enabled = reader.ReadElementContentAsBoolean(); - break; - - case "EndBoundary": - t.EndBoundary = reader.ReadElementContentAsDateTime(); - break; - - case "RandomDelay": - ((ITriggerDelay)t).Delay = Task.StringToTimeSpan(reader.ReadElementContentAsString()); - break; - - case "StartBoundary": - t.StartBoundary = reader.ReadElementContentAsDateTime(); - break; - - case "Repetition": - XmlSerializationHelper.ReadObject(reader, t.Repetition); - break; - - case "ScheduleByDay": - case "ScheduleByWeek": - case "ScheduleByMonth": - case "ScheduleByMonthDayOfWeek": - calReaderProc(reader); - break; - - default: - reader.Skip(); - break; - } - } - reader.ReadEndElement(); - } - } - - internal sealed class RepetitionPatternConverter : TypeConverter - { - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string) || base.CanConvertTo(context, destinationType); - - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) - { - var rp = (RepetitionPattern)value; - if (destinationType != typeof(string)) return base.ConvertTo(context, culture, value, destinationType); - if (rp.Interval == TimeSpan.Zero) return ""; - var sduration = rp.Duration == TimeSpan.Zero ? Properties.Resources.TriggerDuration0 : string.Format(Properties.Resources.TriggerDurationNot0Short, Trigger.GetBestTimeSpanString(rp.Duration)); - return string.Format(Properties.Resources.TriggerRepetitionShort, Trigger.GetBestTimeSpanString(rp.Interval), sduration); - } - } + /// Triggers the task on a daily schedule. + Daily = 2, + + /// Triggers the task on a weekly schedule. + Weekly = 3, + + /// Triggers the task on a monthly schedule. + Monthly = 4, + + /// Triggers the task on a monthly day-of-week schedule. + MonthlyDOW = 5, + + /// Triggers the task when the computer goes into an idle state. + Idle = 6, + + /// Triggers the task when the task is registered. Version 1.2 only. + Registration = 7, + + /// Triggers the task when the computer boots. + Boot = 8, + + /// Triggers the task when a specific user logs on. + Logon = 9, + + /// Triggers the task when a specific user session state changes. Version 1.2 only. + SessionStateChange = 11, + + /// Triggers the custom trigger. Version 1.3 only. + Custom = 12 + } + + /// Values for week of month (first, second, ..., last) + [Flags] + public enum WhichWeek : short + { + /// First week of the month + FirstWeek = 1, + + /// Second week of the month + SecondWeek = 2, + + /// Third week of the month + ThirdWeek = 4, + + /// Fourth week of the month + FourthWeek = 8, + + /// Last week of the month + LastWeek = 0x10, + + /// Every week of the month + AllWeeks = 0x1F + } + + /// Interface that categorizes the trigger as a calendar trigger. + public interface ICalendarTrigger { } + + /// Interface for triggers that support a delay. + public interface ITriggerDelay + { + /// Gets or sets a value that indicates the amount of time before the task is started. + /// The delay duration. + TimeSpan Delay { get; set; } + } + + /// Interface for triggers that support a user identifier. + public interface ITriggerUserId + { + /// Gets or sets the user for the . + string UserId { get; set; } + } + + /// Represents a trigger that starts a task when the system is booted. + /// + /// A BootTrigger will fire when the system starts. It can only be delayed. All triggers that support a delay implement the + /// ITriggerDelay interface. + /// + /// + /// + /// + /// + /// + public sealed class BootTrigger : Trigger, ITriggerDelay + { + /// Creates an unbound instance of a . + public BootTrigger() : base(TaskTriggerType.Boot) { } + + internal BootTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.OnSystemStart) + { + } + + internal BootTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan Delay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IBootTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IBootTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(Delay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() => Resources.TriggerBoot1; + } + + /// + /// Represents a custom trigger. This class is based on undocumented features and may change. This type of trigger is only + /// available for reading custom triggers. It cannot be used to create custom triggers. + /// + public sealed class CustomTrigger : Trigger, ITriggerDelay + { + private readonly NamedValueCollection nvc = new NamedValueCollection(); + private TimeSpan delay = TimeSpan.MinValue; + private string name = string.Empty; + + internal CustomTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets a value that indicates the amount of time between the trigger events and when the task is started. + /// This value cannot be set. + public TimeSpan Delay + { + get => delay; + set => throw new NotImplementedException(); + } + + /// Gets the name of the custom trigger type. + /// The name of the XML element representing this custom trigger. + public string Name => name; + + /// Gets the properties from the XML definition if possible. + [XmlArray, XmlArrayItem("Property")] + public NamedValueCollection Properties => nvc; + + /// Clones this instance. + /// This method will always throw an exception. + /// CustomTrigger cannot be cloned due to OS restrictions. + public override object Clone() => throw new InvalidOperationException("CustomTrigger cannot be cloned due to OS restrictions."); + + /// Updates custom properties from XML provided by definition. + /// The XML from the TaskDefinition. + internal void UpdateFromXml(string xml) + { + nvc.Clear(); + try + { + var xmlDoc = new System.Xml.XmlDocument(); + xmlDoc.LoadXml(xml); + var nsmgr = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable); + nsmgr.AddNamespace("n", "http://schemas.microsoft.com/windows/2004/02/mit/task"); + var elem = xmlDoc.DocumentElement?.SelectSingleNode("n:Triggers/*[@id='" + Id + "']", nsmgr); + if (elem == null) + { + var nodes = xmlDoc.GetElementsByTagName("WnfStateChangeTrigger"); + if (nodes.Count == 1) + elem = nodes[0]; + } + + if (elem == null) return; + + name = elem.LocalName; + foreach (System.Xml.XmlNode node in elem.ChildNodes) + { + switch (node.LocalName) + { + case "Delay": + delay = Task.StringToTimeSpan(node.InnerText); + break; + + case "StartBoundary": + case "Enabled": + case "EndBoundary": + case "ExecutionTimeLimit": + break; + + default: + nvc.Add(node.LocalName, node.InnerText); + break; + } + } + } + catch { /* ignored */ } + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() => Resources.TriggerCustom1; + } + + /// + /// Represents a trigger that starts a task based on a daily schedule. For example, the task starts at a specific time every day, every + /// other day, every third day, and so on. + /// + /// A DailyTrigger will fire at a specified time every day or interval of days. + /// + /// + /// + /// + /// + [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class DailyTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable + { + /// Creates an unbound instance of a . + /// Interval between the days in the schedule. + public DailyTrigger(short daysInterval = 1) : base(TaskTriggerType.Daily) => DaysInterval = daysInterval; + + internal DailyTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunDaily) + { + if (v1TriggerData.Data.daily.DaysInterval == 0) + v1TriggerData.Data.daily.DaysInterval = 1; + } + + internal DailyTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Sets or retrieves the interval between the days in the schedule. + [DefaultValue(1)] + public short DaysInterval + { + get + { + if (v2Trigger != null) + return ((IDailyTrigger)v2Trigger).DaysInterval; + return (short)v1TriggerData.Data.daily.DaysInterval; + } + set + { + if (v2Trigger != null) + ((IDailyTrigger)v2Trigger).DaysInterval = value; + else + { + v1TriggerData.Data.daily.DaysInterval = (ushort)value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(DaysInterval)] = value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a delay time that is randomly added to the start time of the trigger. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan RandomDelay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IDailyTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IDailyTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RandomDelay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a value that indicates the amount of time before the task is started. + /// The delay duration. + TimeSpan ITriggerDelay.Delay + { + get => RandomDelay; + set => RandomDelay = value; + } + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public override void CopyProperties(Trigger sourceTrigger) + { + base.CopyProperties(sourceTrigger); + if (sourceTrigger is DailyTrigger dt) + { + DaysInterval = dt.DaysInterval; + } + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Trigger other) => other is DailyTrigger dt && base.Equals(dt) && DaysInterval == dt.DaysInterval; + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() => DaysInterval == 1 ? + string.Format(Resources.TriggerDaily1, AdjustToLocal(StartBoundary)) : + string.Format(Resources.TriggerDaily2, AdjustToLocal(StartBoundary), DaysInterval); + + private void ReadMyXml(System.Xml.XmlReader reader) + { + reader.ReadStartElement("ScheduleByDay"); + if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "DaysInterval") + // ReSharper disable once AssignNullToNotNullAttribute + DaysInterval = (short)reader.ReadElementContentAs(typeof(short), null); + reader.Read(); + reader.ReadEndElement(); + } + + private void WriteMyXml(System.Xml.XmlWriter writer) + { + writer.WriteStartElement("ScheduleByDay"); + writer.WriteElementString("DaysInterval", DaysInterval.ToString()); + writer.WriteEndElement(); + } + } + + /// + /// Represents a trigger that starts a task when a system event occurs. Only available for Task Scheduler 2.0 on Windows Vista or + /// Windows Server 2003 and later. + /// + /// The EventTrigger runs when a system event fires. + /// + /// + ///"; + /// eTrigger.ValueQueries.Add("Name", "Value"); + ///]]> + /// + /// + [XmlType(IncludeInSchema = false)] + public sealed class EventTrigger : Trigger, ITriggerDelay + { + private NamedValueCollection nvc; + + /// Creates an unbound instance of a . + public EventTrigger() : base(TaskTriggerType.Event) { } + + /// Initializes an unbound instance of the class and sets a basic event. + /// The event's log. + /// The event's source. Can be null. + /// The event's id. Can be null. + public EventTrigger(string log, string source, int? eventId) : this() => SetBasic(log, source, eventId); + + internal EventTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + public TimeSpan Delay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IEventTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IEventTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); + else + unboundValues[nameof(Delay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the XPath query string that identifies the event that fires the trigger. + [DefaultValue(null)] + public string Subscription + { + get => v2Trigger != null ? ((IEventTrigger)v2Trigger).Subscription : GetUnboundValueOrDefault(nameof(Subscription)); + set + { + if (v2Trigger != null) + ((IEventTrigger)v2Trigger).Subscription = value; + else + unboundValues[nameof(Subscription)] = value; + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets a collection of named XPath queries. Each query in the collection is applied to the last matching event XML returned from + /// the subscription query specified in the Subscription property. The name of the query can be used as a variable in the message of + /// a action. + /// + [XmlArray] + [XmlArrayItem("Value", typeof(NameValuePair))] + public NamedValueCollection ValueQueries => nvc ??= v2Trigger == null ? new NamedValueCollection() : new NamedValueCollection(((IEventTrigger)v2Trigger).ValueQueries); + + /// Builds an event log XML query string based on the input parameters. + /// The event's log. + /// The event's source. Can be null. + /// The event's id. Can be null. + /// XML query string. + /// log + public static string BuildQuery(string log, string source, int? eventId) + { + var sb = new StringBuilder(); + if (string.IsNullOrEmpty(log)) + throw new ArgumentNullException(nameof(log)); + sb.AppendFormat(""); + return sb.ToString(); + } + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public override void CopyProperties(Trigger sourceTrigger) + { + base.CopyProperties(sourceTrigger); + if (sourceTrigger is EventTrigger et) + { + Subscription = et.Subscription; + et.ValueQueries.CopyTo(ValueQueries); + } + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Trigger other) => other is EventTrigger et && base.Equals(et) && Subscription == et.Subscription; + + /// Gets basic event information. + /// The event's log. + /// The event's source. Can be null. + /// The event's id. Can be null. + /// true if subscription represents a basic event, false if not. + public bool GetBasic(out string log, out string source, out int? eventId) + { + log = source = null; + eventId = null; + if (!string.IsNullOrEmpty(Subscription)) + { + using var str = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(Subscription)); + using var rdr = new System.Xml.XmlTextReader(str) + { + WhitespaceHandling = System.Xml.WhitespaceHandling.None + }; + try + { + rdr.MoveToContent(); + rdr.ReadStartElement("QueryList"); + if (rdr.Name == "Query" && rdr.MoveToAttribute("Path")) + { + var path = rdr.Value; + if (rdr.MoveToElement() && rdr.ReadToDescendant("Select") && path.Equals(rdr["Path"], StringComparison.InvariantCultureIgnoreCase)) + { + var content = rdr.ReadString(); + var m = System.Text.RegularExpressions.Regex.Match(content, + @"\*(?:\[System\[(?:Provider\[\@Name='(?[^']+)'\])?(?:\s+and\s+)?(?:EventID=(?\d+))?\]\])", + System.Text.RegularExpressions.RegexOptions.IgnoreCase | + System.Text.RegularExpressions.RegexOptions.Compiled | + System.Text.RegularExpressions.RegexOptions.Singleline | + System.Text.RegularExpressions.RegexOptions.IgnorePatternWhitespace); + if (m.Success) + { + log = path; + if (m.Groups["s"].Success) + source = m.Groups["s"].Value; + if (m.Groups["e"].Success) + eventId = Convert.ToInt32(m.Groups["e"].Value); + return true; + } + } + } + } + catch { /* ignored */ } + } + return false; + } + + /// + /// Sets the subscription for a basic event. This will replace the contents of the property and clear all + /// entries in the property. + /// + /// The event's log. + /// The event's source. Can be null. + /// The event's id. Can be null. + public void SetBasic([NotNull] string log, string source, int? eventId) + { + ValueQueries.Clear(); + Subscription = BuildQuery(log, source, eventId); + } + + internal override void Bind(ITaskDefinition iTaskDef) + { + base.Bind(iTaskDef); + nvc?.Bind(((IEventTrigger)v2Trigger).ValueQueries); + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() + { + if (!GetBasic(out var log, out var source, out var id)) + return Resources.TriggerEvent1; + var sb = new StringBuilder(); + sb.AppendFormat(Resources.TriggerEventBasic1, log); + if (!string.IsNullOrEmpty(source)) + sb.AppendFormat(Resources.TriggerEventBasic2, source); + if (id.HasValue) + sb.AppendFormat(Resources.TriggerEventBasic3, id.Value); + return sb.ToString(); + } + } + + /// + /// Represents a trigger that starts a task when the computer goes into an idle state. For information about idle conditions, see Task + /// Idle Conditions. + /// + /// + /// An IdleTrigger will fire when the system becomes idle. It is generally a good practice to set a limit on how long it can run using + /// the ExecutionTimeLimit property. + /// + /// + /// + /// + /// + /// + public sealed class IdleTrigger : Trigger + { + /// Creates an unbound instance of a . + public IdleTrigger() : base(TaskTriggerType.Idle) { } + + internal IdleTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.OnIdle) + { + } + + internal IdleTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() => Resources.TriggerIdle1; + } + + /// + /// Represents a trigger that starts a task when a user logs on. When the Task Scheduler service starts, all logged-on users are + /// enumerated and any tasks registered with logon triggers that match the logged on user are run. Not available on Task Scheduler 1.0. + /// + /// + /// A LogonTrigger will fire after a user logs on. It can only be delayed. Under V2, you can specify which user it applies to. + /// + /// + /// + /// + /// + /// + public sealed class LogonTrigger : Trigger, ITriggerDelay, ITriggerUserId + { + /// Creates an unbound instance of a . + public LogonTrigger() : base(TaskTriggerType.Logon) { } + + internal LogonTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.OnLogon) + { + } + + internal LogonTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan Delay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((ILogonTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((ILogonTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(Delay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets The identifier of the user. For example, "MyDomain\MyName" or for a local account, "Administrator". + /// This property can be in one of the following formats: + /// • User name or SID: The task is started when the user logs on to the computer. + /// • NULL: The task is started when any user logs on to the computer. + /// + /// + /// If you want a task to be triggered when any member of a group logs on to the computer rather than when a specific user logs on, + /// then do not assign a value to the LogonTrigger.UserId property. Instead, create a logon trigger with an empty + /// LogonTrigger.UserId property and assign a value to the principal for the task using the Principal.GroupId property. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + [XmlIgnore] + public string UserId + { + get => v2Trigger != null ? ((ILogonTrigger)v2Trigger).UserId : GetUnboundValueOrDefault(nameof(UserId)); + set + { + if (v2Trigger != null) + ((ILogonTrigger)v2Trigger).UserId = value; + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(UserId)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() + { + var user = string.IsNullOrEmpty(UserId) ? Resources.TriggerAnyUser : UserId; + return string.Format(Resources.TriggerLogon1, user); + } + } + + /// + /// Represents a trigger that starts a task on a monthly day-of-week schedule. For example, the task starts on every first Thursday, May + /// through October. + /// + [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class MonthlyDOWTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable + { + /// Creates an unbound instance of a . + /// The days of the week. + /// The months of the year. + /// The weeks of the month. + public MonthlyDOWTrigger(DaysOfTheWeek daysOfWeek = DaysOfTheWeek.Sunday, MonthsOfTheYear monthsOfYear = MonthsOfTheYear.AllMonths, WhichWeek weeksOfMonth = WhichWeek.FirstWeek) : base(TaskTriggerType.MonthlyDOW) + { + DaysOfWeek = daysOfWeek; + MonthsOfYear = monthsOfYear; + WeeksOfMonth = weeksOfMonth; + } + + internal MonthlyDOWTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunMonthlyDOW) + { + if (v1TriggerData.Data.monthlyDOW.Months == 0) + v1TriggerData.Data.monthlyDOW.Months = MonthsOfTheYear.AllMonths; + if (v1TriggerData.Data.monthlyDOW.DaysOfTheWeek == 0) + v1TriggerData.Data.monthlyDOW.DaysOfTheWeek = DaysOfTheWeek.Sunday; + } + + internal MonthlyDOWTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets the days of the week during which the task runs. + [DefaultValue(0)] + public DaysOfTheWeek DaysOfWeek + { + get => v2Trigger != null + ? (DaysOfTheWeek)((IMonthlyDOWTrigger)v2Trigger).DaysOfWeek + : v1TriggerData.Data.monthlyDOW.DaysOfTheWeek; + set + { + if (v2Trigger != null) + ((IMonthlyDOWTrigger)v2Trigger).DaysOfWeek = (short)value; + else + { + v1TriggerData.Data.monthlyDOW.DaysOfTheWeek = value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(DaysOfWeek)] = (short)value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the months of the year during which the task runs. + [DefaultValue(0)] + public MonthsOfTheYear MonthsOfYear + { + get => v2Trigger != null + ? (MonthsOfTheYear)((IMonthlyDOWTrigger)v2Trigger).MonthsOfYear + : v1TriggerData.Data.monthlyDOW.Months; + set + { + if (v2Trigger != null) + ((IMonthlyDOWTrigger)v2Trigger).MonthsOfYear = (short)value; + else + { + v1TriggerData.Data.monthlyDOW.Months = value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(MonthsOfYear)] = (short)value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a delay time that is randomly added to the start time of the trigger. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan RandomDelay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IMonthlyDOWTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IMonthlyDOWTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RandomDelay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the task runs on the last week of the month. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(false)] + [XmlIgnore] + public bool RunOnLastWeekOfMonth + { + get => ((IMonthlyDOWTrigger)v2Trigger)?.RunOnLastWeekOfMonth ?? GetUnboundValueOrDefault(nameof(RunOnLastWeekOfMonth), false); + set + { + if (v2Trigger != null) + ((IMonthlyDOWTrigger)v2Trigger).RunOnLastWeekOfMonth = value; + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RunOnLastWeekOfMonth)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the weeks of the month during which the task runs. + [DefaultValue(0)] + public WhichWeek WeeksOfMonth + { + get + { + if (v2Trigger == null) + return v1Trigger != null + ? v1TriggerData.Data.monthlyDOW.V2WhichWeek + : GetUnboundValueOrDefault(nameof(WeeksOfMonth), WhichWeek.FirstWeek); + var ww = (WhichWeek)((IMonthlyDOWTrigger)v2Trigger).WeeksOfMonth; + // Following addition give accurate results for confusing RunOnLastWeekOfMonth property (thanks kbergeron) + if (((IMonthlyDOWTrigger)v2Trigger).RunOnLastWeekOfMonth) + ww |= WhichWeek.LastWeek; + return ww; + } + set + { + // In Windows 10, the native library no longer acknowledges the LastWeek value and requires the RunOnLastWeekOfMonth to be + // expressly set. I think this is wrong so I am correcting their changed functionality. (thanks @SebastiaanPolfliet) + if (value.IsFlagSet(WhichWeek.LastWeek)) + RunOnLastWeekOfMonth = true; + if (v2Trigger != null) + { + ((IMonthlyDOWTrigger)v2Trigger).WeeksOfMonth = (short)value; + } + else + { + try + { + v1TriggerData.Data.monthlyDOW.V2WhichWeek = value; + } + catch (NotV1SupportedException) + { + if (v1Trigger != null) throw; + } + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(WeeksOfMonth)] = (short)value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a value that indicates the amount of time before the task is started. + /// The delay duration. + TimeSpan ITriggerDelay.Delay + { + get => RandomDelay; + set => RandomDelay = value; + } + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public override void CopyProperties(Trigger sourceTrigger) + { + base.CopyProperties(sourceTrigger); + if (sourceTrigger is MonthlyDOWTrigger mt) + { + DaysOfWeek = mt.DaysOfWeek; + MonthsOfYear = mt.MonthsOfYear; + try { RunOnLastWeekOfMonth = mt.RunOnLastWeekOfMonth; } catch { /* ignored */ } + WeeksOfMonth = mt.WeeksOfMonth; + } + if (sourceTrigger is MonthlyTrigger m) + MonthsOfYear = m.MonthsOfYear; + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Trigger other) => other is MonthlyDOWTrigger mt && base.Equals(other) && DaysOfWeek == mt.DaysOfWeek && + MonthsOfYear == mt.MonthsOfYear && WeeksOfMonth == mt.WeeksOfMonth && v1Trigger == null && RunOnLastWeekOfMonth == mt.RunOnLastWeekOfMonth; + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() + { + var ww = TaskEnumGlobalizer.GetString(WeeksOfMonth); + var days = TaskEnumGlobalizer.GetString(DaysOfWeek); + var months = TaskEnumGlobalizer.GetString(MonthsOfYear); + return string.Format(Resources.TriggerMonthlyDOW1, AdjustToLocal(StartBoundary), ww, days, months); + } + + /// Reads the subclass XML for V1 streams. + /// The reader. + private void ReadMyXml([NotNull] System.Xml.XmlReader reader) + { + reader.ReadStartElement("ScheduleByMonthDayOfWeek"); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "Weeks": + reader.Read(); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + if (reader.LocalName == "Week") + { + var wk = reader.ReadElementContentAsString(); + if (wk == "Last") + WeeksOfMonth = WhichWeek.LastWeek; + else + { + WeeksOfMonth = (int.Parse(wk)) switch + { + 1 => WhichWeek.FirstWeek, + 2 => WhichWeek.SecondWeek, + 3 => WhichWeek.ThirdWeek, + 4 => WhichWeek.FourthWeek, + _ => throw new System.Xml.XmlException("Week element must contain a 1-4 or Last as content."), + }; + } + } + } + reader.ReadEndElement(); + break; + + case "DaysOfWeek": + reader.Read(); + DaysOfWeek = 0; + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + try + { + DaysOfWeek |= (DaysOfTheWeek)Enum.Parse(typeof(DaysOfTheWeek), reader.LocalName); + } + catch + { + throw new System.Xml.XmlException("Invalid days of the week element."); + } + reader.Read(); + } + reader.ReadEndElement(); + break; + + case "Months": + reader.Read(); + MonthsOfYear = 0; + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + try + { + MonthsOfYear |= (MonthsOfTheYear)Enum.Parse(typeof(MonthsOfTheYear), reader.LocalName); + } + catch + { + throw new System.Xml.XmlException("Invalid months of the year element."); + } + reader.Read(); + } + reader.ReadEndElement(); + break; + + default: + reader.Skip(); + break; + } + } + reader.ReadEndElement(); + } + + /// Writes the subclass XML for V1 streams. + /// The writer. + private void WriteMyXml([NotNull] System.Xml.XmlWriter writer) + { + writer.WriteStartElement("ScheduleByMonthDayOfWeek"); + + writer.WriteStartElement("Weeks"); + if ((WeeksOfMonth & WhichWeek.FirstWeek) == WhichWeek.FirstWeek) + writer.WriteElementString("Week", "1"); + if ((WeeksOfMonth & WhichWeek.SecondWeek) == WhichWeek.SecondWeek) + writer.WriteElementString("Week", "2"); + if ((WeeksOfMonth & WhichWeek.ThirdWeek) == WhichWeek.ThirdWeek) + writer.WriteElementString("Week", "3"); + if ((WeeksOfMonth & WhichWeek.FourthWeek) == WhichWeek.FourthWeek) + writer.WriteElementString("Week", "4"); + if ((WeeksOfMonth & WhichWeek.LastWeek) == WhichWeek.LastWeek) + writer.WriteElementString("Week", "Last"); + writer.WriteEndElement(); + + writer.WriteStartElement("DaysOfWeek"); + foreach (DaysOfTheWeek e in Enum.GetValues(typeof(DaysOfTheWeek))) + if (e != DaysOfTheWeek.AllDays && (DaysOfWeek & e) == e) + writer.WriteElementString(e.ToString(), null); + writer.WriteEndElement(); + + writer.WriteStartElement("Months"); + foreach (MonthsOfTheYear e in Enum.GetValues(typeof(MonthsOfTheYear))) + if (e != MonthsOfTheYear.AllMonths && (MonthsOfYear & e) == e) + writer.WriteElementString(e.ToString(), null); + writer.WriteEndElement(); + + writer.WriteEndElement(); + } + } + + /// + /// Represents a trigger that starts a job based on a monthly schedule. For example, the task starts on specific days of specific months. + /// + [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class MonthlyTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable + { + /// Creates an unbound instance of a . + /// + /// The day of the month. This must be a value between 1 and 32. If this value is set to 32, then the value will be set and no days will be added regardless of the month. + /// + /// The months of the year. + public MonthlyTrigger(int dayOfMonth = 1, MonthsOfTheYear monthsOfYear = MonthsOfTheYear.AllMonths) : base(TaskTriggerType.Monthly) + { + if (dayOfMonth < 1 || dayOfMonth > 32) throw new ArgumentOutOfRangeException(nameof(dayOfMonth)); + if (!monthsOfYear.IsValidFlagValue()) throw new ArgumentOutOfRangeException(nameof(monthsOfYear)); + if (dayOfMonth == 32) + { + DaysOfMonth = new int[0]; + RunOnLastDayOfMonth = true; + } + else + DaysOfMonth = new[] { dayOfMonth }; + MonthsOfYear = monthsOfYear; + } + + internal MonthlyTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunMonthly) + { + if (v1TriggerData.Data.monthlyDate.Months == 0) + v1TriggerData.Data.monthlyDate.Months = MonthsOfTheYear.AllMonths; + if (v1TriggerData.Data.monthlyDate.Days == 0) + v1TriggerData.Data.monthlyDate.Days = 1; + } + + internal MonthlyTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets the days of the month during which the task runs. + public int[] DaysOfMonth + { + get => v2Trigger != null ? MaskToIndices(((IMonthlyTrigger)v2Trigger).DaysOfMonth) : MaskToIndices((int)v1TriggerData.Data.monthlyDate.Days); + set + { + var mask = IndicesToMask(value); + if (v2Trigger != null) + ((IMonthlyTrigger)v2Trigger).DaysOfMonth = mask; + else + { + v1TriggerData.Data.monthlyDate.Days = (uint)mask; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(DaysOfMonth)] = mask; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the months of the year during which the task runs. + [DefaultValue(0)] + public MonthsOfTheYear MonthsOfYear + { + get => v2Trigger != null + ? (MonthsOfTheYear)((IMonthlyTrigger)v2Trigger).MonthsOfYear + : v1TriggerData.Data.monthlyDOW.Months; + set + { + if (v2Trigger != null) + ((IMonthlyTrigger)v2Trigger).MonthsOfYear = (short)value; + else + { + v1TriggerData.Data.monthlyDOW.Months = value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(MonthsOfYear)] = (short)value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a delay time that is randomly added to the start time of the trigger. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan RandomDelay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IMonthlyTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IMonthlyTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RandomDelay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a Boolean value that indicates that the task runs on the last day of the month. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(false)] + [XmlIgnore] + public bool RunOnLastDayOfMonth + { + get => ((IMonthlyTrigger)v2Trigger)?.RunOnLastDayOfMonth ?? GetUnboundValueOrDefault(nameof(RunOnLastDayOfMonth), false); + set + { + if (v2Trigger != null) + ((IMonthlyTrigger)v2Trigger).RunOnLastDayOfMonth = value; + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RunOnLastDayOfMonth)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a value that indicates the amount of time before the task is started. + /// The delay duration. + TimeSpan ITriggerDelay.Delay + { + get => RandomDelay; + set => RandomDelay = value; + } + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public override void CopyProperties(Trigger sourceTrigger) + { + base.CopyProperties(sourceTrigger); + if (sourceTrigger is MonthlyTrigger mt) + { + DaysOfMonth = mt.DaysOfMonth; + MonthsOfYear = mt.MonthsOfYear; + try { RunOnLastDayOfMonth = mt.RunOnLastDayOfMonth; } catch { /* ignored */ } + } + if (sourceTrigger is MonthlyDOWTrigger mdt) + MonthsOfYear = mdt.MonthsOfYear; + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Trigger other) => other is MonthlyTrigger mt && base.Equals(mt) && ListsEqual(DaysOfMonth, mt.DaysOfMonth) && + MonthsOfYear == mt.MonthsOfYear && v1Trigger == null && RunOnLastDayOfMonth == mt.RunOnLastDayOfMonth; + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() + { + var days = string.Join(Resources.ListSeparator, Array.ConvertAll(DaysOfMonth, i => i.ToString())); + if (RunOnLastDayOfMonth) + days += (days.Length == 0 ? "" : Resources.ListSeparator) + Resources.WWLastWeek; + var months = TaskEnumGlobalizer.GetString(MonthsOfYear); + return string.Format(Resources.TriggerMonthly1, AdjustToLocal(StartBoundary), days, months); + } + + /// + /// Converts an array of bit indices into a mask with bits turned ON at every index contained in the array. Indices must be from 1 + /// to 32 and bits are numbered the same. + /// + /// An array with an element for each bit of the mask which is ON. + /// An integer to be interpreted as a mask. + private static int IndicesToMask(int[] indices) + { + if (indices is null || indices.Length == 0) return 0; + var mask = 0; + foreach (var index in indices) + { + if (index < 1 || index > 31) throw new ArgumentException("Days must be in the range 1..31"); + mask |= 1 << (index - 1); + } + return mask; + } + + /// Compares two collections. + /// Item type of collections. + /// The first collection. + /// The second collection + /// true if the collections values are equal; false otherwise. + private static bool ListsEqual(ICollection left, ICollection right) where T : IComparable + { + if (left == null && right == null) return true; + if (left == null || right == null) return false; + if (left.Count != right.Count) return false; + List l1 = new List(left), l2 = new List(right); + l1.Sort(); l2.Sort(); + for (var i = 0; i < l1.Count; i++) + if (l1[i].CompareTo(l2[i]) != 0) return false; + return true; + } + + /// + /// Convert an integer representing a mask to an array where each element contains the index of a bit that is ON in the mask. Bits + /// are considered to number from 1 to 32. + /// + /// An integer to be interpreted as a mask. + /// An array with an element for each bit of the mask which is ON. + private static int[] MaskToIndices(int mask) + { + //count bits in mask + var cnt = 0; + for (var i = 0; mask >> i > 0; i++) + cnt += (1 & (mask >> i)); + //allocate return array with one entry for each bit + var indices = new int[cnt]; + //fill array with bit indices + cnt = 0; + for (var i = 0; mask >> i > 0; i++) + if ((1 & (mask >> i)) == 1) + indices[cnt++] = i + 1; + return indices; + } + + /// Reads the subclass XML for V1 streams. + /// The reader. + private void ReadMyXml([NotNull] System.Xml.XmlReader reader) + { + reader.ReadStartElement("ScheduleByMonth"); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "DaysOfMonth": + reader.Read(); + var days = new List(); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + if (reader.LocalName != "Day") continue; + var sday = reader.ReadElementContentAsString(); + if (sday.Equals("Last", StringComparison.InvariantCultureIgnoreCase)) continue; + var day = int.Parse(sday); + if (day >= 1 && day <= 31) + days.Add(day); + } + DaysOfMonth = days.ToArray(); + reader.ReadEndElement(); + break; + + case "Months": + reader.Read(); + MonthsOfYear = 0; + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + try + { + MonthsOfYear |= (MonthsOfTheYear)Enum.Parse(typeof(MonthsOfTheYear), reader.LocalName); + } + catch + { + throw new System.Xml.XmlException("Invalid months of the year element."); + } + reader.Read(); + } + reader.ReadEndElement(); + break; + + default: + reader.Skip(); + break; + } + } + reader.ReadEndElement(); + } + + private void WriteMyXml([NotNull] System.Xml.XmlWriter writer) + { + writer.WriteStartElement("ScheduleByMonth"); + + writer.WriteStartElement("DaysOfMonth"); + foreach (var day in DaysOfMonth) + writer.WriteElementString("Day", day.ToString()); + writer.WriteEndElement(); + + writer.WriteStartElement("Months"); + foreach (MonthsOfTheYear e in Enum.GetValues(typeof(MonthsOfTheYear))) + if (e != MonthsOfTheYear.AllMonths && (MonthsOfYear & e) == e) + writer.WriteElementString(e.ToString(), null); + writer.WriteEndElement(); + + writer.WriteEndElement(); + } + } + + /// + /// Represents a trigger that starts a task when the task is registered or updated. Not available on Task Scheduler 1.0. Only + /// available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and later. + /// + /// The RegistrationTrigger will fire after the task is registered (saved). It is advisable to put in a delay. + /// + /// + /// + /// + /// + [XmlType(IncludeInSchema = false)] + public sealed class RegistrationTrigger : Trigger, ITriggerDelay + { + /// Creates an unbound instance of a . + public RegistrationTrigger() : base(TaskTriggerType.Registration) { } + + internal RegistrationTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan Delay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IRegistrationTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IRegistrationTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(Delay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() => Resources.TriggerRegistration1; + } + + /// Defines how often the task is run and how long the repetition pattern is repeated after the task is started. + /// This can be used directly or by assignment for a . + /// + /// + /// + /// + /// + [XmlRoot("Repetition", Namespace = TaskDefinition.tns, IsNullable = true)] + [TypeConverter(typeof(RepetitionPatternConverter))] + public sealed class RepetitionPattern : IDisposable, IXmlSerializable, IEquatable, INotifyPropertyChanged + { + private readonly Trigger pTrigger; + private readonly IRepetitionPattern v2Pattern; + private TimeSpan unboundInterval = TimeSpan.Zero, unboundDuration = TimeSpan.Zero; + private bool unboundStopAtDurationEnd; + + /// Initializes a new instance of the class. + /// + /// The amount of time between each restart of the task. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. + /// + /// + /// The duration of how long the pattern is repeated. The minimum time allowed is one minute. If TimeSpan.Zero is specified, + /// the pattern is repeated indefinitely. + /// + /// + /// If set to true the running instance of the task is stopped at the end of repetition pattern duration. + /// + public RepetitionPattern(TimeSpan interval, TimeSpan duration, bool stopAtDurationEnd = false) + { + Interval = interval; + Duration = duration; + StopAtDurationEnd = stopAtDurationEnd; + } + + internal RepetitionPattern([NotNull] Trigger parent) + { + pTrigger = parent; + if (pTrigger?.v2Trigger != null) + v2Pattern = pTrigger.v2Trigger.Repetition; + } + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets or sets how long the pattern is repeated. + /// + /// The duration that the pattern is repeated. The minimum time allowed is one minute. If TimeSpan.Zero is specified, the + /// pattern is repeated indefinitely. + /// + /// If you specify a repetition duration for a task, you must also specify the repetition interval. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + public TimeSpan Duration + { + get => v2Pattern != null + ? Task.StringToTimeSpan(v2Pattern.Duration) + : (pTrigger != null ? TimeSpan.FromMinutes(pTrigger.v1TriggerData.MinutesDuration) : unboundDuration); + set + { + if (value.Ticks < 0 || value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) + throw new ArgumentOutOfRangeException(nameof(Duration)); + if (v2Pattern != null) + { + v2Pattern.Duration = Task.TimeSpanToString(value); + } + else if (pTrigger != null) + { + pTrigger.v1TriggerData.MinutesDuration = (uint)value.TotalMinutes; + Bind(); + } + else + unboundDuration = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the amount of time between each restart of the task. + /// + /// The amount of time between each restart of the task. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. + /// + /// If you specify a repetition duration for a task, you must also specify the repetition interval. + /// + /// The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. + /// + [DefaultValue(typeof(TimeSpan), "00:00:00")] + public TimeSpan Interval + { + get => v2Pattern != null + ? Task.StringToTimeSpan(v2Pattern.Interval) + : (pTrigger != null ? TimeSpan.FromMinutes(pTrigger.v1TriggerData.MinutesInterval) : unboundInterval); + set + { + if (value.Ticks < 0 || (v2Pattern != null || pTrigger == null) && value != TimeSpan.Zero && (value < TimeSpan.FromMinutes(1) || value > TimeSpan.FromDays(31))) + throw new ArgumentOutOfRangeException(nameof(Interval)); + if (v2Pattern != null) + { + v2Pattern.Interval = Task.TimeSpanToString(value); + } + else if (pTrigger != null) + { + if (value != TimeSpan.Zero && value < TimeSpan.FromMinutes(1)) + throw new ArgumentOutOfRangeException(nameof(Interval)); + pTrigger.v1TriggerData.MinutesInterval = (uint)value.TotalMinutes; + Bind(); + } + else + unboundInterval = value; + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets a Boolean value that indicates if a running instance of the task is stopped at the end of repetition pattern duration. + /// + [DefaultValue(false)] + public bool StopAtDurationEnd + { + get + { + if (v2Pattern != null) + return v2Pattern.StopAtDurationEnd; + if (pTrigger != null) + return (pTrigger.v1TriggerData.Flags & TaskTriggerFlags.KillAtDurationEnd) == TaskTriggerFlags.KillAtDurationEnd; + return unboundStopAtDurationEnd; + } + set + { + if (v2Pattern != null) + v2Pattern.StopAtDurationEnd = value; + else if (pTrigger != null) + { + if (value) + pTrigger.v1TriggerData.Flags |= TaskTriggerFlags.KillAtDurationEnd; + else + pTrigger.v1TriggerData.Flags &= ~TaskTriggerFlags.KillAtDurationEnd; + Bind(); + } + else + unboundStopAtDurationEnd = value; + OnNotifyPropertyChanged(); + } + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Pattern != null) Marshal.ReleaseComObject(v2Pattern); + } + + /// Determines whether the specified , is equal to this instance. + /// The to compare with this instance. + /// true if the specified is equal to this instance; otherwise, false. + // ReSharper disable once BaseObjectEqualsIsObjectEquals + public override bool Equals(object obj) => obj is RepetitionPattern pattern ? Equals(pattern) : base.Equals(obj); + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public bool Equals(RepetitionPattern other) => other != null && Duration == other.Duration && Interval == other.Interval && StopAtDurationEnd == other.StopAtDurationEnd; + + /// Returns a hash code for this instance. + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + public override int GetHashCode() => new { A = Duration, B = Interval, C = StopAtDurationEnd }.GetHashCode(); + + /// Determines whether any properties for this have been set. + /// true if properties have been set; otherwise, false. + public bool IsSet() + { + if (v2Pattern != null) + return v2Pattern.StopAtDurationEnd || !string.IsNullOrEmpty(v2Pattern.Duration) || !string.IsNullOrEmpty(v2Pattern.Interval); + if (pTrigger != null) + return (pTrigger.v1TriggerData.Flags & TaskTriggerFlags.KillAtDurationEnd) == TaskTriggerFlags.KillAtDurationEnd || pTrigger.v1TriggerData.MinutesDuration > 0 || pTrigger.v1TriggerData.MinutesInterval > 0; + return false; + } + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) + { + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); + XmlSerializationHelper.ReadObjectProperties(reader, this, ReadXmlConverter); + reader.ReadEndElement(); + } + else + reader.Skip(); + } + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => XmlSerializationHelper.WriteObjectProperties(writer, this); + + internal void Bind() + { + if (pTrigger.v1Trigger != null) + pTrigger.SetV1TriggerData(); + else if (pTrigger.v2Trigger != null) + { + if (pTrigger.v1TriggerData.MinutesInterval != 0) + v2Pattern.Interval = $"PT{pTrigger.v1TriggerData.MinutesInterval}M"; + if (pTrigger.v1TriggerData.MinutesDuration != 0) + v2Pattern.Duration = $"PT{pTrigger.v1TriggerData.MinutesDuration}M"; + v2Pattern.StopAtDurationEnd = (pTrigger.v1TriggerData.Flags & TaskTriggerFlags.KillAtDurationEnd) == TaskTriggerFlags.KillAtDurationEnd; + } + } + + internal void Set([NotNull] RepetitionPattern value) + { + Duration = value.Duration; + Interval = value.Interval; + StopAtDurationEnd = value.StopAtDurationEnd; + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private bool ReadXmlConverter(System.Reflection.PropertyInfo pi, object obj, ref object value) + { + if (pi.Name != "Interval" || !(value is TimeSpan span) || span.Equals(TimeSpan.Zero) || Duration > span) + return false; + Duration = span.Add(TimeSpan.FromMinutes(1)); + return true; + } + } + + /// + /// Triggers tasks for console connect or disconnect, remote connect or disconnect, or workstation lock or unlock notifications. + /// Only available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and later. + /// + /// + /// The SessionStateChangeTrigger will fire after six different system events: connecting or disconnecting locally or remotely, or + /// locking or unlocking the session. + /// + /// + /// + /// + /// + /// + [XmlType(IncludeInSchema = false)] + public sealed class SessionStateChangeTrigger : Trigger, ITriggerDelay, ITriggerUserId + { + /// Creates an unbound instance of a . + public SessionStateChangeTrigger() : base(TaskTriggerType.SessionStateChange) { } + + /// Initializes a new instance of the class. + /// The state change. + /// The user identifier. + public SessionStateChangeTrigger(TaskSessionStateChangeType stateChange, string userId = null) : this() { StateChange = stateChange; UserId = userId; } + + internal SessionStateChangeTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets a value that indicates the amount of time between when the system is booted and when the task is started. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + public TimeSpan Delay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((ISessionStateChangeTrigger)v2Trigger).Delay) : GetUnboundValueOrDefault(nameof(Delay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((ISessionStateChangeTrigger)v2Trigger).Delay = Task.TimeSpanToString(value); + else + unboundValues[nameof(Delay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the kind of Terminal Server session change that would trigger a task launch. + [DefaultValue(1)] + public TaskSessionStateChangeType StateChange + { + get => ((ISessionStateChangeTrigger)v2Trigger)?.StateChange ?? GetUnboundValueOrDefault(nameof(StateChange), TaskSessionStateChangeType.ConsoleConnect); + set + { + if (v2Trigger != null) + ((ISessionStateChangeTrigger)v2Trigger).StateChange = value; + else + unboundValues[nameof(StateChange)] = value; + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the user for the Terminal Server session. When a session state change is detected for this user, a task is started. + /// + [DefaultValue(null)] + public string UserId + { + get => v2Trigger != null ? ((ISessionStateChangeTrigger)v2Trigger).UserId : GetUnboundValueOrDefault(nameof(UserId)); + set + { + if (v2Trigger != null) + ((ISessionStateChangeTrigger)v2Trigger).UserId = value; + else + unboundValues[nameof(UserId)] = value; + OnNotifyPropertyChanged(); + } + } + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public override void CopyProperties(Trigger sourceTrigger) + { + base.CopyProperties(sourceTrigger); + if (sourceTrigger is SessionStateChangeTrigger st && !StateChangeIsSet()) + StateChange = st.StateChange; + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Trigger other) => other is SessionStateChangeTrigger st && base.Equals(st) && StateChange == st.StateChange; + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() + { + var str = Resources.ResourceManager.GetString("TriggerSession" + StateChange.ToString()); + var user = string.IsNullOrEmpty(UserId) ? Resources.TriggerAnyUser : UserId; + if (StateChange != TaskSessionStateChangeType.SessionLock && StateChange != TaskSessionStateChangeType.SessionUnlock) + user = string.Format(Resources.TriggerSessionUserSession, user); + return string.Format(str, user); + } + + /// Returns a value indicating if the StateChange property has been set. + /// StateChange property has been set. + private bool StateChangeIsSet() => v2Trigger != null || (unboundValues?.ContainsKey("StateChange") ?? false); + } + + /// Represents a trigger that starts a task at a specific date and time. + /// A TimeTrigger runs at a specified date and time. + /// + /// + /// + /// + /// + public sealed class TimeTrigger : Trigger, ITriggerDelay, ICalendarTrigger + { + /// Creates an unbound instance of a . + public TimeTrigger() : base(TaskTriggerType.Time) { } + + /// Creates an unbound instance of a and assigns the execution time. + /// Date and time for the trigger to fire. + public TimeTrigger(DateTime startBoundary) : base(TaskTriggerType.Time) => StartBoundary = startBoundary; + + internal TimeTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunOnce) + { + } + + internal TimeTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets a delay time that is randomly added to the start time of the trigger. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan RandomDelay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((ITimeTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((ITimeTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RandomDelay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a value that indicates the amount of time before the task is started. + /// The delay duration. + TimeSpan ITriggerDelay.Delay + { + get => RandomDelay; + set => RandomDelay = value; + } + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() => string.Format(Resources.TriggerTime1, AdjustToLocal(StartBoundary)); + } + + /// + /// Abstract base class which provides the common properties that are inherited by all trigger classes. A trigger can be created using + /// the or the method. + /// + public abstract partial class Trigger : IDisposable, ICloneable, IEquatable, IComparable, IComparable, INotifyPropertyChanged + { + internal const string V2BoundaryDateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFK"; + internal static readonly CultureInfo DefaultDateCulture = CultureInfo.CreateSpecificCulture("en-US"); + + internal ITaskTrigger v1Trigger; + internal TaskTrigger v1TriggerData; + internal ITrigger v2Trigger; + + /// In testing and may change. Do not use until officially introduced into library. + protected Dictionary unboundValues = new Dictionary(); + + private static bool? foundTimeSpan2; + private static Type timeSpan2Type; + private readonly TaskTriggerType ttype; + private RepetitionPattern repititionPattern; + + internal Trigger([NotNull] ITaskTrigger trigger, V1.TaskTriggerType type) + { + v1Trigger = trigger; + v1TriggerData = trigger.GetTrigger(); + v1TriggerData.Type = type; + ttype = ConvertFromV1TriggerType(type); + } + + internal Trigger([NotNull] ITrigger iTrigger) + { + v2Trigger = iTrigger; + ttype = iTrigger.Type; + if (string.IsNullOrEmpty(v2Trigger.StartBoundary) && this is ICalendarTrigger) + StartBoundary = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified); + } + + internal Trigger(TaskTriggerType triggerType) + { + ttype = triggerType; + + v1TriggerData.TriggerSize = (ushort)Marshal.SizeOf(typeof(TaskTrigger)); + if (ttype != TaskTriggerType.Registration && ttype != TaskTriggerType.Event && ttype != TaskTriggerType.SessionStateChange) + v1TriggerData.Type = ConvertToV1TriggerType(ttype); + + if (this is ICalendarTrigger) + StartBoundary = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified); + } + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets or sets a Boolean value that indicates whether the trigger is enabled. + public bool Enabled + { + get => v2Trigger?.Enabled ?? GetUnboundValueOrDefault(nameof(Enabled), !v1TriggerData.Flags.IsFlagSet(TaskTriggerFlags.Disabled)); + set + { + if (v2Trigger != null) + v2Trigger.Enabled = value; + else + { + v1TriggerData.Flags = v1TriggerData.Flags.SetFlags(TaskTriggerFlags.Disabled, !value); + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(Enabled)] = value; + } + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the date and time when the trigger is deactivated. The trigger cannot start the task after it is deactivated. + /// While the maximum value for this property is , the Windows Task Scheduler management + /// application that is part of the OS will fail if this value is greater than December 31, 9998. + /// + /// + /// + /// Version 1 (1.1 on all systems prior to Vista) of the native library only allows for the Day, Month and Year values of the structure. + /// + /// + /// Version 2 (1.2 or higher) of the native library only allows for both date and time and all values. + /// However, the user interface and methods will always show the time translated to local time. The + /// library makes every attempt to maintain the Kind value. When using the UI elements provided in the TaskSchedulerEditor library, + /// the "Synchronize across time zones" checkbox will be checked if the Kind is Local or Utc. If the Kind is Unspecified and the + /// user selects the checkbox, the Kind will be changed to Utc and the time adjusted from the value displayed as the local time. + /// + /// + [DefaultValue(typeof(DateTime), "9999-12-31T23:59:59.9999999")] + public DateTime EndBoundary + { + get + { + if (v2Trigger != null) + return string.IsNullOrEmpty(v2Trigger.EndBoundary) ? DateTime.MaxValue : DateTime.Parse(v2Trigger.EndBoundary, DefaultDateCulture); + return GetUnboundValueOrDefault(nameof(EndBoundary), v1TriggerData.EndDate.GetValueOrDefault(DateTime.MaxValue)); + } + set + { + if (v2Trigger != null) + { + if (value <= StartBoundary) + throw new ArgumentException(Resources.Error_TriggerEndBeforeStart); + v2Trigger.EndBoundary = value == DateTime.MaxValue ? null : value.ToString(V2BoundaryDateFormat, DefaultDateCulture); + } + else + { + v1TriggerData.EndDate = value == DateTime.MaxValue ? (DateTime?)null : value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(EndBoundary)] = value; + } + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets or sets the maximum amount of time that the task launched by this trigger is allowed to run. Not available with Task + /// Scheduler 1.0. + /// + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan ExecutionTimeLimit + { + get => v2Trigger != null ? Task.StringToTimeSpan(v2Trigger.ExecutionTimeLimit) : GetUnboundValueOrDefault(nameof(ExecutionTimeLimit), TimeSpan.Zero); + set + { + if (v2Trigger != null) + v2Trigger.ExecutionTimeLimit = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(ExecutionTimeLimit)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the identifier for the trigger. Cannot set with Task Scheduler 1.0. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(null)] + [XmlIgnore] + public string Id + { + get => v2Trigger != null ? v2Trigger.Id : GetUnboundValueOrDefault(nameof(Id)); + set + { + if (v2Trigger != null) + v2Trigger.Id = value; + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(Id)] = value; + OnNotifyPropertyChanged(); + } + } + + /// + /// Gets a instance that indicates how often the task is run and how long the repetition pattern is + /// repeated after the task is started. + /// + public RepetitionPattern Repetition + { + get => repititionPattern ??= new RepetitionPattern(this); + set + { + Repetition.Set(value); + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the date and time when the trigger is activated. + /// + /// + /// Version 1 (1.1 on all systems prior to Vista) of the native library only allows for values where the is unspecified. If the DateTime value Kind is then it will be used as + /// is. If the DateTime value Kind is then it will be converted to the local time and then used. + /// + /// + /// Version 2 (1.2 or higher) of the native library only allows for all values. However, the user + /// interface and methods will always show the time translated to local time. The library makes + /// every attempt to maintain the Kind value. When using the UI elements provided in the TaskSchedulerEditor library, the + /// "Synchronize across time zones" checkbox will be checked if the Kind is Local or Utc. If the Kind is Unspecified and the user + /// selects the checkbox, the Kind will be changed to Utc and the time adjusted from the value displayed as the local time. + /// + /// + /// Under Version 2, when converting the string used in the native library for this value (ITrigger.Startboundary) this library will + /// behave as follows: + /// + /// + /// YYYY-MM-DDTHH:MM:SS format uses DateTimeKind.Unspecified and the time specified. + /// + /// + /// YYYY-MM-DDTHH:MM:SSZ format uses DateTimeKind.Utc and the time specified as the GMT time. + /// + /// + /// YYYY-MM-DDTHH:MM:SS±HH:MM format uses DateTimeKind.Local and the time specified in that time zone. + /// + /// + /// + /// + public DateTime StartBoundary + { + get + { + if (v2Trigger == null) return GetUnboundValueOrDefault(nameof(StartBoundary), v1TriggerData.BeginDate); + if (string.IsNullOrEmpty(v2Trigger.StartBoundary)) + return DateTime.MinValue; + var ret = DateTime.Parse(v2Trigger.StartBoundary, DefaultDateCulture); + if (v2Trigger.StartBoundary.EndsWith("Z")) + ret = ret.ToUniversalTime(); + return ret; + } + set + { + if (v2Trigger != null) + { + if (value > EndBoundary) + throw new ArgumentException(Resources.Error_TriggerEndBeforeStart); + v2Trigger.StartBoundary = value == DateTime.MinValue ? null : value.ToString(V2BoundaryDateFormat, DefaultDateCulture); + } + else + { + v1TriggerData.BeginDate = value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(StartBoundary)] = value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets the type of the trigger. + /// The of the trigger. + [XmlIgnore] + public TaskTriggerType TriggerType => ttype; + + /// Creates the specified trigger. + /// Type of the trigger to instantiate. + /// of specified type. + public static Trigger CreateTrigger(TaskTriggerType triggerType) + { + switch (triggerType) + { + case TaskTriggerType.Boot: + return new BootTrigger(); + + case TaskTriggerType.Daily: + return new DailyTrigger(); + + case TaskTriggerType.Event: + return new EventTrigger(); + + case TaskTriggerType.Idle: + return new IdleTrigger(); + + case TaskTriggerType.Logon: + return new LogonTrigger(); + + case TaskTriggerType.Monthly: + return new MonthlyTrigger(); + + case TaskTriggerType.MonthlyDOW: + return new MonthlyDOWTrigger(); + + case TaskTriggerType.Registration: + return new RegistrationTrigger(); + + case TaskTriggerType.SessionStateChange: + return new SessionStateChangeTrigger(); + + case TaskTriggerType.Time: + return new TimeTrigger(); + + case TaskTriggerType.Weekly: + return new WeeklyTrigger(); + + case TaskTriggerType.Custom: + break; + + default: + throw new ArgumentOutOfRangeException(nameof(triggerType), triggerType, null); + } + return null; + } + + /// Creates a new that is an unbound copy of this instance. + /// A new that is an unbound copy of this instance. + public virtual object Clone() + { + var ret = CreateTrigger(TriggerType); + ret.CopyProperties(this); + return ret; + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current + /// instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// + /// An object to compare with this instance. + /// A value that indicates the relative order of the objects being compared. + public int CompareTo(Trigger other) => string.Compare(Id, other?.Id, StringComparison.InvariantCulture); + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public virtual void CopyProperties(Trigger sourceTrigger) + { + if (sourceTrigger == null) + return; + Enabled = sourceTrigger.Enabled; + EndBoundary = sourceTrigger.EndBoundary; + try { ExecutionTimeLimit = sourceTrigger.ExecutionTimeLimit; } + catch { /* ignored */ } + Id = sourceTrigger.Id; + Repetition.Duration = sourceTrigger.Repetition.Duration; + Repetition.Interval = sourceTrigger.Repetition.Interval; + Repetition.StopAtDurationEnd = sourceTrigger.Repetition.StopAtDurationEnd; + StartBoundary = sourceTrigger.StartBoundary; + if (sourceTrigger is ITriggerDelay delay && this is ITriggerDelay) + try { ((ITriggerDelay)this).Delay = delay.Delay; } + catch { /* ignored */ } + if (sourceTrigger is ITriggerUserId id && this is ITriggerUserId) + try { ((ITriggerUserId)this).UserId = id.UserId; } + catch { /* ignored */ } + } + + /// Releases all resources used by this class. + public virtual void Dispose() + { + if (v2Trigger != null) + Marshal.ReleaseComObject(v2Trigger); + if (v1Trigger != null) + Marshal.ReleaseComObject(v1Trigger); + } + + /// Determines whether the specified , is equal to this instance. + /// The to compare with this instance. + /// true if the specified is equal to this instance; otherwise, false. + // ReSharper disable once BaseObjectEqualsIsObjectEquals + public override bool Equals(object obj) => obj is Trigger trigger ? Equals(trigger) : base.Equals(obj); + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public virtual bool Equals(Trigger other) + { + if (other == null) return false; + var ret = TriggerType == other.TriggerType && Enabled == other.Enabled && EndBoundary == other.EndBoundary && + ExecutionTimeLimit == other.ExecutionTimeLimit && Id == other.Id && Repetition.Equals(other.Repetition) && + StartBoundary == other.StartBoundary; + if (other is ITriggerDelay delay && this is ITriggerDelay) + try { ret = ret && ((ITriggerDelay)this).Delay == delay.Delay; } + catch { /* ignored */ } + if (other is ITriggerUserId id && this is ITriggerUserId) + try { ret = ret && ((ITriggerUserId)this).UserId == id.UserId; } + catch { /* ignored */ } + return ret; + } + + /// Returns a hash code for this instance. + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + public override int GetHashCode() => new + { + A = TriggerType, + B = Enabled, + C = EndBoundary, + D = ExecutionTimeLimit, + E = Id, + F = Repetition, + G = StartBoundary, + H = (this as ITriggerDelay)?.Delay ?? TimeSpan.Zero, + I = (this as ITriggerUserId)?.UserId + }.GetHashCode(); + + /// Sets the repetition. + /// + /// The amount of time between each restart of the task. The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. + /// + /// + /// The duration of how long the pattern is repeated. The minimum time allowed is one minute. If TimeSpan.Zero is specified, + /// the pattern is repeated indefinitely. + /// + /// + /// if set to true the running instance of the task is stopped at the end of repetition pattern duration. + /// + [Obsolete("Set the Repetition property directly with a new instance of RepetitionPattern.", false)] + public void SetRepetition(TimeSpan interval, TimeSpan duration, bool stopAtDurationEnd = true) + { + Repetition.Duration = duration; + Repetition.Interval = interval; + Repetition.StopAtDurationEnd = stopAtDurationEnd; + } + + /// Returns a string representing this trigger. + /// String value of trigger. + public override string ToString() => v1Trigger != null ? v1Trigger.GetTriggerString() : V2GetTriggerString() + V2BaseTriggerString(); + + /// Returns a that represents this trigger in a specific language. + /// The language of the resulting string. + /// String value of trigger. + public virtual string ToString([NotNull] CultureInfo culture) + { + using (new CultureSwitcher(culture)) + return ToString(); + } + + int IComparable.CompareTo(object obj) => CompareTo(obj as Trigger); + + internal static DateTime AdjustToLocal(DateTime dt) => dt.Kind == DateTimeKind.Utc ? dt.ToLocalTime() : dt; + + internal static V1.TaskTriggerType ConvertToV1TriggerType(TaskTriggerType type) + { + if (type == TaskTriggerType.Registration || type == TaskTriggerType.Event || type == TaskTriggerType.SessionStateChange) + throw new NotV1SupportedException(); + var tv1 = (int)type - 1; + if (tv1 >= 7) tv1--; + return (V1.TaskTriggerType)tv1; + } + + internal static Trigger CreateTrigger([NotNull] ITaskTrigger trigger) => CreateTrigger(trigger, trigger.GetTrigger().Type); + + internal static Trigger CreateTrigger([NotNull] ITaskTrigger trigger, V1.TaskTriggerType triggerType) + { + Trigger t = triggerType switch + { + V1.TaskTriggerType.RunOnce => new TimeTrigger(trigger), + V1.TaskTriggerType.RunDaily => new DailyTrigger(trigger), + V1.TaskTriggerType.RunWeekly => new WeeklyTrigger(trigger), + V1.TaskTriggerType.RunMonthly => new MonthlyTrigger(trigger), + V1.TaskTriggerType.RunMonthlyDOW => new MonthlyDOWTrigger(trigger), + V1.TaskTriggerType.OnIdle => new IdleTrigger(trigger), + V1.TaskTriggerType.OnSystemStart => new BootTrigger(trigger), + V1.TaskTriggerType.OnLogon => new LogonTrigger(trigger), + _ => throw new ArgumentOutOfRangeException(nameof(triggerType), triggerType, null), + }; + return t; + } + + internal static Trigger CreateTrigger([NotNull] ITrigger iTrigger, ITaskDefinition iDef = null) + { + switch (iTrigger.Type) + { + case TaskTriggerType.Boot: + return new BootTrigger((IBootTrigger)iTrigger); + + case TaskTriggerType.Daily: + return new DailyTrigger((IDailyTrigger)iTrigger); + + case TaskTriggerType.Event: + return new EventTrigger((IEventTrigger)iTrigger); + + case TaskTriggerType.Idle: + return new IdleTrigger((IIdleTrigger)iTrigger); + + case TaskTriggerType.Logon: + return new LogonTrigger((ILogonTrigger)iTrigger); + + case TaskTriggerType.Monthly: + return new MonthlyTrigger((IMonthlyTrigger)iTrigger); + + case TaskTriggerType.MonthlyDOW: + return new MonthlyDOWTrigger((IMonthlyDOWTrigger)iTrigger); + + case TaskTriggerType.Registration: + return new RegistrationTrigger((IRegistrationTrigger)iTrigger); + + case TaskTriggerType.SessionStateChange: + return new SessionStateChangeTrigger((ISessionStateChangeTrigger)iTrigger); + + case TaskTriggerType.Time: + return new TimeTrigger((ITimeTrigger)iTrigger); + + case TaskTriggerType.Weekly: + return new WeeklyTrigger((IWeeklyTrigger)iTrigger); + + case TaskTriggerType.Custom: + var ct = new CustomTrigger(iTrigger); + if (iDef != null) + try { ct.UpdateFromXml(iDef.XmlText); } catch { /* ignored */ } + return ct; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + /// Gets the best time span string. + /// The to display. + /// Either the full string representation created by TimeSpan2 or the default TimeSpan representation. + internal static string GetBestTimeSpanString(TimeSpan span) + { + // See if the TimeSpan2 assembly is accessible + if (!foundTimeSpan2.HasValue) + { + try + { + foundTimeSpan2 = false; + timeSpan2Type = ReflectionHelper.LoadType("System.TimeSpan2", "TimeSpan2.dll"); + if (timeSpan2Type != null) + foundTimeSpan2 = true; + } + catch { /* ignored */ } + } + + // If the TimeSpan2 assembly is available, try to call the ToString("f") method and return the result. + if (foundTimeSpan2 == true && timeSpan2Type != null) + { + try + { + return ReflectionHelper.InvokeMethod(timeSpan2Type, new object[] { span }, "ToString", "f"); + } + catch { /* ignored */ } + } + + return span.ToString(); + } + + internal virtual void Bind([NotNull] ITask iTask) + { + if (v1Trigger == null) + { + v1Trigger = iTask.CreateTrigger(out var _); + } + SetV1TriggerData(); + } + + internal virtual void Bind([NotNull] ITaskDefinition iTaskDef) + { + var iTriggers = iTaskDef.Triggers; + v2Trigger = iTriggers.Create(ttype); + Marshal.ReleaseComObject(iTriggers); + if ((unboundValues.TryGetValue("StartBoundary", out var dt) ? (DateTime)dt : StartBoundary) > (unboundValues.TryGetValue("EndBoundary", out dt) ? (DateTime)dt : EndBoundary)) + throw new ArgumentException(Resources.Error_TriggerEndBeforeStart); + foreach (var key in unboundValues.Keys) + { + try + { + var o = unboundValues[key]; + CheckBindValue(key, ref o); + v2Trigger.GetType().InvokeMember(key, System.Reflection.BindingFlags.SetProperty, null, v2Trigger, new[] { o }); + } + catch (System.Reflection.TargetInvocationException tie) when (tie.InnerException != null) { throw tie.InnerException; } + catch { /* ignored */ } + } + unboundValues.Clear(); + unboundValues = null; + + repititionPattern = new RepetitionPattern(this); + repititionPattern.Bind(); + } + + /// Assigns the unbound TriggerData structure to the V1 trigger instance. + internal void SetV1TriggerData() + { + if (v1TriggerData.MinutesInterval != 0 && v1TriggerData.MinutesInterval >= v1TriggerData.MinutesDuration) + throw new ArgumentException("Trigger.Repetition.Interval must be less than Trigger.Repetition.Duration under Task Scheduler 1.0."); + if (v1TriggerData.EndDate <= v1TriggerData.BeginDate) + throw new ArgumentException(Resources.Error_TriggerEndBeforeStart); + if (v1TriggerData.BeginDate == DateTime.MinValue) + v1TriggerData.BeginDate = DateTime.Now; + v1Trigger?.SetTrigger(ref v1TriggerData); + System.Diagnostics.Debug.WriteLine(v1TriggerData); + } + + /// Checks the bind value for any conversion. + /// The key (property) name. + /// The value. + protected virtual void CheckBindValue(string key, ref object o) + { + if (o is TimeSpan ts) + o = Task.TimeSpanToString(ts); + if (o is DateTime dt) + { + if (key == "EndBoundary" && dt == DateTime.MaxValue || key == "StartBoundary" && dt == DateTime.MinValue) + o = null; + else + o = dt.ToString(V2BoundaryDateFormat, DefaultDateCulture); + } + } + + /// Gets the unbound value or a default. + /// Return type. + /// The property name. + /// The default value if not found in unbound value list. + /// The unbound value, if set, or the default value. + protected T GetUnboundValueOrDefault(string prop, T def = default) => unboundValues.TryGetValue(prop, out var val) ? (T)val : def; + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + protected void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected virtual string V2GetTriggerString() => string.Empty; + + private static TaskTriggerType ConvertFromV1TriggerType(V1.TaskTriggerType v1Type) + { + var tv2 = (int)v1Type + 1; + if (tv2 > 6) tv2++; + return (TaskTriggerType)tv2; + } + + private string V2BaseTriggerString() + { + var ret = new StringBuilder(); + if (Repetition.Interval != TimeSpan.Zero) + { + var sduration = Repetition.Duration == TimeSpan.Zero ? Resources.TriggerDuration0 : string.Format(Resources.TriggerDurationNot0, GetBestTimeSpanString(Repetition.Duration)); + ret.AppendFormat(Resources.TriggerRepetition, GetBestTimeSpanString(Repetition.Interval), sduration); + } + if (EndBoundary != DateTime.MaxValue) + ret.AppendFormat(Resources.TriggerEndBoundary, AdjustToLocal(EndBoundary)); + if (ret.Length > 0) + ret.Insert(0, Resources.HyphenSeparator); + return ret.ToString(); + } + } + + /// + /// Represents a trigger that starts a task based on a weekly schedule. For example, the task starts at 8:00 A.M. on a specific day of + /// the week every week or every other week. + /// + /// A WeeklyTrigger runs at a specified time on specified days of the week every week or interval of weeks. + /// + /// + /// + /// + /// + [XmlRoot("CalendarTrigger", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class WeeklyTrigger : Trigger, ICalendarTrigger, ITriggerDelay, IXmlSerializable + { + /// Creates an unbound instance of a . + /// The days of the week. + /// The interval between the weeks in the schedule. + public WeeklyTrigger(DaysOfTheWeek daysOfWeek = DaysOfTheWeek.Sunday, short weeksInterval = 1) : base(TaskTriggerType.Weekly) + { + DaysOfWeek = daysOfWeek; + WeeksInterval = weeksInterval; + } + + internal WeeklyTrigger([NotNull] ITaskTrigger iTrigger) : base(iTrigger, V1.TaskTriggerType.RunWeekly) + { + if (v1TriggerData.Data.weekly.DaysOfTheWeek == 0) + v1TriggerData.Data.weekly.DaysOfTheWeek = DaysOfTheWeek.Sunday; + if (v1TriggerData.Data.weekly.WeeksInterval == 0) + v1TriggerData.Data.weekly.WeeksInterval = 1; + } + + internal WeeklyTrigger([NotNull] ITrigger iTrigger) : base(iTrigger) + { + } + + /// Gets or sets the days of the week on which the task runs. + [DefaultValue(0)] + public DaysOfTheWeek DaysOfWeek + { + get => v2Trigger != null + ? (DaysOfTheWeek)((IWeeklyTrigger)v2Trigger).DaysOfWeek + : v1TriggerData.Data.weekly.DaysOfTheWeek; + set + { + if (v2Trigger != null) + ((IWeeklyTrigger)v2Trigger).DaysOfWeek = (short)value; + else + { + v1TriggerData.Data.weekly.DaysOfTheWeek = value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(DaysOfWeek)] = (short)value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a delay time that is randomly added to the start time of the trigger. + /// Not supported under Task Scheduler 1.0. + [DefaultValue(typeof(TimeSpan), "00:00:00")] + [XmlIgnore] + public TimeSpan RandomDelay + { + get => v2Trigger != null ? Task.StringToTimeSpan(((IWeeklyTrigger)v2Trigger).RandomDelay) : GetUnboundValueOrDefault(nameof(RandomDelay), TimeSpan.Zero); + set + { + if (v2Trigger != null) + ((IWeeklyTrigger)v2Trigger).RandomDelay = Task.TimeSpanToString(value); + else if (v1Trigger != null) + throw new NotV1SupportedException(); + else + unboundValues[nameof(RandomDelay)] = value; + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets the interval between the weeks in the schedule. + [DefaultValue(1)] + public short WeeksInterval + { + get => ((IWeeklyTrigger)v2Trigger)?.WeeksInterval ?? (short)v1TriggerData.Data.weekly.WeeksInterval; + set + { + if (v2Trigger != null) + ((IWeeklyTrigger)v2Trigger).WeeksInterval = value; + else + { + v1TriggerData.Data.weekly.WeeksInterval = (ushort)value; + if (v1Trigger != null) + SetV1TriggerData(); + else + unboundValues[nameof(WeeksInterval)] = value; + } + OnNotifyPropertyChanged(); + } + } + + /// Gets or sets a value that indicates the amount of time before the task is started. + /// The delay duration. + TimeSpan ITriggerDelay.Delay + { + get => RandomDelay; + set => RandomDelay = value; + } + + /// + /// Copies the properties from another the current instance. This will not copy any properties associated with + /// any derived triggers except those supporting the interface. + /// + /// The source . + public override void CopyProperties(Trigger sourceTrigger) + { + base.CopyProperties(sourceTrigger); + if (sourceTrigger is WeeklyTrigger wt) + { + DaysOfWeek = wt.DaysOfWeek; + WeeksInterval = wt.WeeksInterval; + } + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public override bool Equals(Trigger other) => other is WeeklyTrigger wt && base.Equals(wt) && DaysOfWeek == wt.DaysOfWeek && WeeksInterval == wt.WeeksInterval; + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) => CalendarTrigger.ReadXml(reader, this, ReadMyXml); + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) => CalendarTrigger.WriteXml(writer, this, WriteMyXml); + + /// Gets the non-localized trigger string for V2 triggers. + /// String describing the trigger. + protected override string V2GetTriggerString() + { + var days = TaskEnumGlobalizer.GetString(DaysOfWeek); + return string.Format(WeeksInterval == 1 ? Resources.TriggerWeekly1Week : Resources.TriggerWeeklyMultWeeks, AdjustToLocal(StartBoundary), days, WeeksInterval); + } + + /// Reads the subclass XML for V1 streams. + /// The reader. + private void ReadMyXml(System.Xml.XmlReader reader) + { + reader.ReadStartElement("ScheduleByWeek"); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "WeeksInterval": + WeeksInterval = (short)reader.ReadElementContentAsInt(); + break; + + case "DaysOfWeek": + reader.Read(); + DaysOfWeek = 0; + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + try + { + DaysOfWeek |= (DaysOfTheWeek)Enum.Parse(typeof(DaysOfTheWeek), reader.LocalName); + } + catch + { + throw new System.Xml.XmlException("Invalid days of the week element."); + } + reader.Read(); + } + reader.ReadEndElement(); + break; + + default: + reader.Skip(); + break; + } + } + reader.ReadEndElement(); + } + + /// Writes the subclass XML for V1 streams. + /// The writer. + private void WriteMyXml(System.Xml.XmlWriter writer) + { + writer.WriteStartElement("ScheduleByWeek"); + + if (WeeksInterval != 1) + writer.WriteElementString("WeeksInterval", WeeksInterval.ToString()); + + writer.WriteStartElement("DaysOfWeek"); + foreach (DaysOfTheWeek e in Enum.GetValues(typeof(DaysOfTheWeek))) + if (e != DaysOfTheWeek.AllDays && (DaysOfWeek & e) == e) + writer.WriteElementString(e.ToString(), null); + writer.WriteEndElement(); + + writer.WriteEndElement(); + } + } + + internal static class CalendarTrigger + { + internal delegate void CalendarXmlReader(System.Xml.XmlReader reader); + + internal delegate void CalendarXmlWriter(System.Xml.XmlWriter writer); + + public static void WriteXml([NotNull] System.Xml.XmlWriter writer, [NotNull] Trigger t, [NotNull] CalendarXmlWriter calWriterProc) + { + if (!t.Enabled) + writer.WriteElementString("Enabled", System.Xml.XmlConvert.ToString(t.Enabled)); + if (t.EndBoundary != DateTime.MaxValue) + writer.WriteElementString("EndBoundary", System.Xml.XmlConvert.ToString(t.EndBoundary, System.Xml.XmlDateTimeSerializationMode.RoundtripKind)); + XmlSerializationHelper.WriteObject(writer, t.Repetition); + writer.WriteElementString("StartBoundary", System.Xml.XmlConvert.ToString(t.StartBoundary, System.Xml.XmlDateTimeSerializationMode.RoundtripKind)); + calWriterProc(writer); + } + + internal static Trigger GetTriggerFromXml([NotNull] System.Xml.XmlReader reader) + { + Trigger t = null; + var xml = reader.ReadOuterXml(); + var match = System.Text.RegularExpressions.Regex.Match(xml, @"\<(?ScheduleBy.+)\>"); + if (match.Success && match.Groups.Count == 2) + { + switch (match.Groups[1].Value) + { + case "ScheduleByDay": + t = new DailyTrigger(); + break; + + case "ScheduleByWeek": + t = new WeeklyTrigger(); + break; + + case "ScheduleByMonth": + t = new MonthlyTrigger(); + break; + + case "ScheduleByMonthDayOfWeek": + t = new MonthlyDOWTrigger(); + break; + } + + if (t != null) + { + using var ms = new System.IO.StringReader(xml); + using var iReader = System.Xml.XmlReader.Create(ms); + ((IXmlSerializable)t).ReadXml(iReader); + } + } + return t; + } + + internal static void ReadXml([NotNull] System.Xml.XmlReader reader, [NotNull] Trigger t, [NotNull] CalendarXmlReader calReaderProc) + { + reader.ReadStartElement("CalendarTrigger", TaskDefinition.tns); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "Enabled": + t.Enabled = reader.ReadElementContentAsBoolean(); + break; + + case "EndBoundary": + t.EndBoundary = reader.ReadElementContentAsDateTime(); + break; + + case "RandomDelay": + ((ITriggerDelay)t).Delay = Task.StringToTimeSpan(reader.ReadElementContentAsString()); + break; + + case "StartBoundary": + t.StartBoundary = reader.ReadElementContentAsDateTime(); + break; + + case "Repetition": + XmlSerializationHelper.ReadObject(reader, t.Repetition); + break; + + case "ScheduleByDay": + case "ScheduleByWeek": + case "ScheduleByMonth": + case "ScheduleByMonthDayOfWeek": + calReaderProc(reader); + break; + + default: + reader.Skip(); + break; + } + } + reader.ReadEndElement(); + } + } + + internal sealed class RepetitionPatternConverter : TypeConverter + { + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string) || base.CanConvertTo(context, destinationType); + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + var rp = (RepetitionPattern)value; + if (destinationType != typeof(string)) return base.ConvertTo(context, culture, value, destinationType); + if (rp.Interval == TimeSpan.Zero) return ""; + var sduration = rp.Duration == TimeSpan.Zero ? Resources.TriggerDuration0 : string.Format(Resources.TriggerDurationNot0Short, Trigger.GetBestTimeSpanString(rp.Duration)); + return string.Format(Resources.TriggerRepetitionShort, Trigger.GetBestTimeSpanString(rp.Interval), sduration); + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TriggerCollection.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TriggerCollection.cs index ac4c51ee6..5cfb8e4af 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/TriggerCollection.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/TriggerCollection.cs @@ -3,11 +3,8 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; using System.Xml.Serialization; using winPEAS.TaskScheduler.TaskEditor.Native; using winPEAS.TaskScheduler.V1; @@ -15,129 +12,129 @@ namespace winPEAS.TaskScheduler { - [XmlRoot("Triggers", Namespace = TaskDefinition.tns, IsNullable = false)] - public sealed class TriggerCollection : IList, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged - { - private const string IndexerName = "Item[]"; - private readonly ITriggerCollection v2Coll; - private bool inV2set; - private ITask v1Task; - private ITaskDefinition v2Def; - - internal TriggerCollection([NotNull] ITask iTask) => v1Task = iTask; - - internal TriggerCollection([NotNull] ITaskDefinition iTaskDef) - { - v2Def = iTaskDef; - v2Coll = v2Def.Triggers; - } - - /// Occurs when a collection changes. - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// Occurs when a property value changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// Gets the number of triggers in the collection. - public int Count => v2Coll?.Count ?? v1Task.GetTriggerCount(); - - bool IList.IsFixedSize => false; - - bool ICollection.IsReadOnly => false; - - bool IList.IsReadOnly => false; - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot => this; - - /// Gets or sets a specified trigger from the collection. - /// The . - /// The id ( ) of the trigger to be retrieved. - /// Specialized instance. - /// - /// - /// - /// Mismatching Id for trigger and lookup. - public Trigger this[[NotNull] string triggerId] - { - get - { - if (string.IsNullOrEmpty(triggerId)) - throw new ArgumentNullException(nameof(triggerId)); - foreach (var t in this) - if (string.Equals(t.Id, triggerId)) - return t; - throw new ArgumentOutOfRangeException(nameof(triggerId)); - } - set - { - if (value == null) - throw new NullReferenceException(); - if (string.IsNullOrEmpty(triggerId)) - throw new ArgumentNullException(nameof(triggerId)); - if (triggerId != value.Id) - throw new InvalidOperationException("Mismatching Id for trigger and lookup."); - var index = IndexOf(triggerId); - if (index >= 0) - { - var orig = this[index].Clone(); - inV2set = true; - try - { - RemoveAt(index); - Insert(index, value); - } - finally - { - inV2set = true; - } - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index)); - } - else - Add(value); - } - } - - /// Gets a specified trigger from the collection. - /// The index of the trigger to be retrieved. - /// Specialized instance. - public Trigger this[int index] - { - get - { - if (v2Coll != null) - return Trigger.CreateTrigger(v2Coll[++index], v2Def); - return Trigger.CreateTrigger(v1Task.GetTrigger((ushort)index)); - } - set - { - if (index < 0 || Count <= index) - throw new ArgumentOutOfRangeException(nameof(index), index, @"Index is not a valid index in the TriggerCollection"); - var orig = this[index].Clone(); - inV2set = true; - try - { - Insert(index, value); - RemoveAt(index + 1); - } - finally - { - inV2set = false; - } - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index)); - } - } - - object IList.this[int index] - { - get => this[index]; - set => this[index] = (Trigger)value; - } - - /*/// + [XmlRoot("Triggers", Namespace = TaskDefinition.tns, IsNullable = false)] + public sealed class TriggerCollection : IList, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged + { + private const string IndexerName = "Item[]"; + private readonly ITriggerCollection v2Coll; + private bool inV2set; + private ITask v1Task; + private ITaskDefinition v2Def; + + internal TriggerCollection([NotNull] ITask iTask) => v1Task = iTask; + + internal TriggerCollection([NotNull] ITaskDefinition iTaskDef) + { + v2Def = iTaskDef; + v2Coll = v2Def.Triggers; + } + + /// Occurs when a collection changes. + public event NotifyCollectionChangedEventHandler CollectionChanged; + + /// Occurs when a property value changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// Gets the number of triggers in the collection. + public int Count => v2Coll?.Count ?? v1Task.GetTriggerCount(); + + bool IList.IsFixedSize => false; + + bool ICollection.IsReadOnly => false; + + bool IList.IsReadOnly => false; + + bool ICollection.IsSynchronized => false; + + object ICollection.SyncRoot => this; + + /// Gets or sets a specified trigger from the collection. + /// The . + /// The id ( ) of the trigger to be retrieved. + /// Specialized instance. + /// + /// + /// + /// Mismatching Id for trigger and lookup. + public Trigger this[[NotNull] string triggerId] + { + get + { + if (string.IsNullOrEmpty(triggerId)) + throw new ArgumentNullException(nameof(triggerId)); + foreach (var t in this) + if (string.Equals(t.Id, triggerId)) + return t; + throw new ArgumentOutOfRangeException(nameof(triggerId)); + } + set + { + if (value == null) + throw new NullReferenceException(); + if (string.IsNullOrEmpty(triggerId)) + throw new ArgumentNullException(nameof(triggerId)); + if (triggerId != value.Id) + throw new InvalidOperationException("Mismatching Id for trigger and lookup."); + var index = IndexOf(triggerId); + if (index >= 0) + { + var orig = this[index].Clone(); + inV2set = true; + try + { + RemoveAt(index); + Insert(index, value); + } + finally + { + inV2set = true; + } + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index)); + } + else + Add(value); + } + } + + /// Gets a specified trigger from the collection. + /// The index of the trigger to be retrieved. + /// Specialized instance. + public Trigger this[int index] + { + get + { + if (v2Coll != null) + return Trigger.CreateTrigger(v2Coll[++index], v2Def); + return Trigger.CreateTrigger(v1Task.GetTrigger((ushort)index)); + } + set + { + if (index < 0 || Count <= index) + throw new ArgumentOutOfRangeException(nameof(index), index, @"Index is not a valid index in the TriggerCollection"); + var orig = this[index].Clone(); + inV2set = true; + try + { + Insert(index, value); + RemoveAt(index + 1); + } + finally + { + inV2set = false; + } + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index)); + } + } + + object IList.this[int index] + { + get => this[index]; + set => this[index] = (Trigger)value; + } + + /*/// /// Add an unbound to the task. derivative to /// add to the task. Bound trigger. unboundTrigger /// is null. @@ -152,387 +149,387 @@ public Trigger Add([NotNull] Trigger unboundTrigger) return unboundTrigger; }*/ - /// Add an unbound to the task. - /// A type derived from . - /// derivative to add to the task. - /// Bound trigger. - /// unboundTrigger is null. - public TTrigger Add([NotNull] TTrigger unboundTrigger) where TTrigger : Trigger - { - if (unboundTrigger == null) - throw new ArgumentNullException(nameof(unboundTrigger)); - if (v2Def != null) - unboundTrigger.Bind(v2Def); - else - unboundTrigger.Bind(v1Task); - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, unboundTrigger)); - return unboundTrigger; - } - - /// Add a new trigger to the collections of triggers for the task. - /// The type of trigger to create. - /// A instance of the specified type. - public Trigger AddNew(TaskTriggerType taskTriggerType) - { - if (v1Task != null) - return Trigger.CreateTrigger(v1Task.CreateTrigger(out _), Trigger.ConvertToV1TriggerType(taskTriggerType)); - - return Trigger.CreateTrigger(v2Coll.Create(taskTriggerType), v2Def); - } - - /// Adds a collection of unbound triggers to the end of the . - /// - /// The triggers to be added to the end of the . The collection itself cannot be null and - /// cannot contain null elements. - /// - /// is null. - public void AddRange([NotNull] IEnumerable triggers) - { - if (triggers == null) - throw new ArgumentNullException(nameof(triggers)); - foreach (var item in triggers) - Add(item); - } - - /// Clears all triggers from the task. - public void Clear() - { - if (v2Coll != null) - v2Coll.Clear(); - else - { - inV2set = true; - try - { - for (var i = Count - 1; i >= 0; i--) - RemoveAt(i); - } - finally - { - inV2set = false; - } - } - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - /// Determines whether the contains a specific value. - /// The object to locate in the . - /// true if is found in the ; otherwise, false. - public bool Contains([NotNull] Trigger item) => Find(a => a.Equals(item)) != null; - - /// Determines whether the specified trigger type is contained in this collection. - /// Type of the trigger. - /// true if the specified trigger type is contained in this collection; otherwise, false. - public bool ContainsType(Type triggerType) => Find(a => a.GetType() == triggerType) != null; - - /// - /// Copies the elements of the to an , starting at a particular index. - /// - /// - /// The one-dimensional that is the destination of the elements copied from . The - /// must have zero-based indexing. - /// - /// The zero-based index in at which copying begins. - public void CopyTo(Trigger[] array, int arrayIndex) => CopyTo(0, array, arrayIndex, Count); - - /// - /// Copies the elements of the to a array, starting at a particular array index. - /// - /// The zero-based index in the source at which copying begins. - /// - /// The array that is the destination of the elements copied from . The array must have zero-based indexing. - /// - /// The zero-based index in array at which copying begins. - /// The number of elements to copy. - /// is null. - /// is less than 0. - /// - /// The number of elements in the source is greater than the available space from to the end of the destination . - /// - public void CopyTo(int index, Trigger[] array, int arrayIndex, int count) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index)); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - if (count < 0 || count > (Count - index)) - throw new ArgumentOutOfRangeException(nameof(count)); - if ((Count - index) > (array.Length - arrayIndex)) - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - for (var i = 0; i < count; i++) - array[arrayIndex + i] = (Trigger)this[index + i].Clone(); - } - - /// Releases all resources used by this class. - public void Dispose() - { - if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); - v2Def = null; - v1Task = null; - } - - /// - /// Searches for an that matches the conditions defined by the specified predicate, and returns the first - /// occurrence within the entire collection. - /// - /// - /// The delegate that defines the conditions of the to search for. - /// - /// - /// The first that matches the conditions defined by the specified predicate, if found; otherwise, null. - /// - public Trigger Find([NotNull] Predicate match) - { - if (match == null) - throw new ArgumentNullException(nameof(match)); - foreach (var item in this) - if (match(item)) return item; - return null; - } - - /// - /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based - /// index of the first occurrence within the collection that starts at the specified index and contains the specified number of elements. - /// - /// The zero-based starting index of the search. - /// The number of elements in the collection to search. - /// The delegate that defines the conditions of the element to search for. - /// - /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. - /// - public int FindIndexOf(int startIndex, int count, [NotNull] Predicate match) - { - if (startIndex < 0 || startIndex >= Count) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - if (startIndex + count > Count) - throw new ArgumentOutOfRangeException(nameof(count)); - if (match == null) - throw new ArgumentNullException(nameof(match)); - for (var i = startIndex; i < startIndex + count; i++) - if (match(this[i])) return i; - return -1; - } - - /// - /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based - /// index of the first occurrence within the collection. - /// - /// The delegate that defines the conditions of the element to search for. - /// - /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. - /// - public int FindIndexOf([NotNull] Predicate match) => FindIndexOf(0, Count, match); - - /// Gets the collection enumerator for this collection. - /// The for this collection. - public IEnumerator GetEnumerator() - { - if (v1Task != null) - return new V1TriggerEnumerator(v1Task); - return new ComEnumerator(() => v2Coll.Count, i => v2Coll[i], o => Trigger.CreateTrigger(o, v2Def)); - } - - /// Determines the index of a specific item in the . - /// The object to locate in the . - /// The index of if found in the list; otherwise, -1. - public int IndexOf([NotNull] Trigger item) => FindIndexOf(a => a.Equals(item)); - - /// Determines the index of a specific item in the . - /// The id ( ) of the trigger to be retrieved. - /// The index of if found in the list; otherwise, -1. - public int IndexOf([NotNull] string triggerId) - { - if (string.IsNullOrEmpty(triggerId)) - throw new ArgumentNullException(triggerId); - return FindIndexOf(a => string.Equals(a.Id, triggerId)); - } - - /// Inserts an trigger at the specified index. - /// The zero-based index at which trigger should be inserted. - /// The trigger to insert into the list. - public void Insert(int index, [NotNull] Trigger trigger) - { - if (trigger == null) - throw new ArgumentNullException(nameof(trigger)); - if (index >= Count) - throw new ArgumentOutOfRangeException(nameof(index)); - - var pushItems = new Trigger[Count - index]; - CopyTo(index, pushItems, 0, Count - index); - for (var j = Count - 1; j >= index; j--) - RemoveAt(j); - Add(trigger); - foreach (var t in pushItems) - Add(t); - } - - /// Removes the first occurrence of a specific object from the . - /// The object to remove from the . - /// - /// true if was successfully removed from the ; otherwise, false. This method - /// also returns false if is not found in the original . - /// - public bool Remove([NotNull] Trigger item) - { - var idx = IndexOf(item); - if (idx != -1) - { - try - { - RemoveAt(idx); - return true; - } - catch { } - } - return false; - } - - /// Removes the trigger at a specified index. - /// Index of trigger to remove. - /// Index out of range. - public void RemoveAt(int index) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index), index, @"Failed to remove Trigger. Index out of range."); - var item = this[index].Clone(); - if (v2Coll != null) - v2Coll.Remove(++index); - else - v1Task.DeleteTrigger((ushort)index); //Remove the trigger from the Task Scheduler - if (!inV2set) - { - OnNotifyPropertyChanged(nameof(Count)); - OnNotifyPropertyChanged(IndexerName); - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); - } - } - - /// Copies the elements of the to a new array. - /// An array containing copies of the elements of the . - public Trigger[] ToArray() - { - var ret = new Trigger[Count]; - CopyTo(ret, 0); - return ret; - } - - /// Returns a that represents the triggers in this collection. - /// A that represents the triggers in this collection. - public override string ToString() - { - if (Count == 1) - return this[0].ToString(); - if (Count > 1) - return Properties.Resources.MultipleTriggers; - return string.Empty; - } - - void ICollection.Add(Trigger item) => Add(item); - - int IList.Add(object value) - { - Add((Trigger)value); - return Count - 1; - } - - bool IList.Contains(object value) => Contains((Trigger)value); - - void ICollection.CopyTo(Array array, int index) - { - if (array != null && array.Rank != 1) - throw new RankException("Multi-dimensional arrays are not supported."); - var src = new Trigger[Count]; - CopyTo(src, 0); - Array.Copy(src, 0, array, index, Count); - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; - - int IList.IndexOf(object value) => IndexOf((Trigger)value); - - void IList.Insert(int index, object value) => Insert(index, (Trigger)value); - - void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) - { - reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); - while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) - { - switch (reader.LocalName) - { - case "BootTrigger": - XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Boot)); - break; - - case "IdleTrigger": - XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Idle)); - break; - - case "TimeTrigger": - XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Time)); - break; - - case "LogonTrigger": - XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Logon)); - break; - - case "CalendarTrigger": - Add(CalendarTrigger.GetTriggerFromXml(reader)); - break; - - default: - reader.Skip(); - break; - } - } - reader.ReadEndElement(); - } - - void IList.Remove(object value) => Remove((Trigger)value); - - void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) - { - foreach (var t in this) - XmlSerializationHelper.WriteObject(writer, t); - } - - internal void Bind() - { - foreach (var t in this) - t.SetV1TriggerData(); - } - - /// Called when a property has changed to notify any attached elements. - /// Name of the property. - private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - private sealed class V1TriggerEnumerator : IEnumerator - { - private short curItem = -1; - private ITask iTask; - - internal V1TriggerEnumerator(ITask task) => iTask = task; - - public Trigger Current => Trigger.CreateTrigger(iTask.GetTrigger((ushort)curItem)); - - object IEnumerator.Current => Current; - - /// Releases all resources used by this class. - public void Dispose() => iTask = null; - - public bool MoveNext() => (++curItem < iTask.GetTriggerCount()); - - public void Reset() => curItem = -1; - } - } + /// Add an unbound to the task. + /// A type derived from . + /// derivative to add to the task. + /// Bound trigger. + /// unboundTrigger is null. + public TTrigger Add([NotNull] TTrigger unboundTrigger) where TTrigger : Trigger + { + if (unboundTrigger == null) + throw new ArgumentNullException(nameof(unboundTrigger)); + if (v2Def != null) + unboundTrigger.Bind(v2Def); + else + unboundTrigger.Bind(v1Task); + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, unboundTrigger)); + return unboundTrigger; + } + + /// Add a new trigger to the collections of triggers for the task. + /// The type of trigger to create. + /// A instance of the specified type. + public Trigger AddNew(TaskTriggerType taskTriggerType) + { + if (v1Task != null) + return Trigger.CreateTrigger(v1Task.CreateTrigger(out _), Trigger.ConvertToV1TriggerType(taskTriggerType)); + + return Trigger.CreateTrigger(v2Coll.Create(taskTriggerType), v2Def); + } + + /// Adds a collection of unbound triggers to the end of the . + /// + /// The triggers to be added to the end of the . The collection itself cannot be null and + /// cannot contain null elements. + /// + /// is null. + public void AddRange([NotNull] IEnumerable triggers) + { + if (triggers == null) + throw new ArgumentNullException(nameof(triggers)); + foreach (var item in triggers) + Add(item); + } + + /// Clears all triggers from the task. + public void Clear() + { + if (v2Coll != null) + v2Coll.Clear(); + else + { + inV2set = true; + try + { + for (var i = Count - 1; i >= 0; i--) + RemoveAt(i); + } + finally + { + inV2set = false; + } + } + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + /// Determines whether the contains a specific value. + /// The object to locate in the . + /// true if is found in the ; otherwise, false. + public bool Contains([NotNull] Trigger item) => Find(a => a.Equals(item)) != null; + + /// Determines whether the specified trigger type is contained in this collection. + /// Type of the trigger. + /// true if the specified trigger type is contained in this collection; otherwise, false. + public bool ContainsType(Type triggerType) => Find(a => a.GetType() == triggerType) != null; + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// + /// The one-dimensional that is the destination of the elements copied from . The + /// must have zero-based indexing. + /// + /// The zero-based index in at which copying begins. + public void CopyTo(Trigger[] array, int arrayIndex) => CopyTo(0, array, arrayIndex, Count); + + /// + /// Copies the elements of the to a array, starting at a particular array index. + /// + /// The zero-based index in the source at which copying begins. + /// + /// The array that is the destination of the elements copied from . The array must have zero-based indexing. + /// + /// The zero-based index in array at which copying begins. + /// The number of elements to copy. + /// is null. + /// is less than 0. + /// + /// The number of elements in the source is greater than the available space from to the end of the destination . + /// + public void CopyTo(int index, Trigger[] array, int arrayIndex, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException(nameof(index)); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + if (count < 0 || count > (Count - index)) + throw new ArgumentOutOfRangeException(nameof(count)); + if ((Count - index) > (array.Length - arrayIndex)) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + for (var i = 0; i < count; i++) + array[arrayIndex + i] = (Trigger)this[index + i].Clone(); + } + + /// Releases all resources used by this class. + public void Dispose() + { + if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); + v2Def = null; + v1Task = null; + } + + /// + /// Searches for an that matches the conditions defined by the specified predicate, and returns the first + /// occurrence within the entire collection. + /// + /// + /// The delegate that defines the conditions of the to search for. + /// + /// + /// The first that matches the conditions defined by the specified predicate, if found; otherwise, null. + /// + public Trigger Find([NotNull] Predicate match) + { + if (match == null) + throw new ArgumentNullException(nameof(match)); + foreach (var item in this) + if (match(item)) return item; + return null; + } + + /// + /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based + /// index of the first occurrence within the collection that starts at the specified index and contains the specified number of elements. + /// + /// The zero-based starting index of the search. + /// The number of elements in the collection to search. + /// The delegate that defines the conditions of the element to search for. + /// + /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. + /// + public int FindIndexOf(int startIndex, int count, [NotNull] Predicate match) + { + if (startIndex < 0 || startIndex >= Count) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + if (startIndex + count > Count) + throw new ArgumentOutOfRangeException(nameof(count)); + if (match == null) + throw new ArgumentNullException(nameof(match)); + for (var i = startIndex; i < startIndex + count; i++) + if (match(this[i])) return i; + return -1; + } + + /// + /// Searches for an that matches the conditions defined by the specified predicate, and returns the zero-based + /// index of the first occurrence within the collection. + /// + /// The delegate that defines the conditions of the element to search for. + /// + /// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1. + /// + public int FindIndexOf([NotNull] Predicate match) => FindIndexOf(0, Count, match); + + /// Gets the collection enumerator for this collection. + /// The for this collection. + public IEnumerator GetEnumerator() + { + if (v1Task != null) + return new V1TriggerEnumerator(v1Task); + return new ComEnumerator(() => v2Coll.Count, i => v2Coll[i], o => Trigger.CreateTrigger(o, v2Def)); + } + + /// Determines the index of a specific item in the . + /// The object to locate in the . + /// The index of if found in the list; otherwise, -1. + public int IndexOf([NotNull] Trigger item) => FindIndexOf(a => a.Equals(item)); + + /// Determines the index of a specific item in the . + /// The id ( ) of the trigger to be retrieved. + /// The index of if found in the list; otherwise, -1. + public int IndexOf([NotNull] string triggerId) + { + if (string.IsNullOrEmpty(triggerId)) + throw new ArgumentNullException(triggerId); + return FindIndexOf(a => string.Equals(a.Id, triggerId)); + } + + /// Inserts an trigger at the specified index. + /// The zero-based index at which trigger should be inserted. + /// The trigger to insert into the list. + public void Insert(int index, [NotNull] Trigger trigger) + { + if (trigger == null) + throw new ArgumentNullException(nameof(trigger)); + if (index >= Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + var pushItems = new Trigger[Count - index]; + CopyTo(index, pushItems, 0, Count - index); + for (var j = Count - 1; j >= index; j--) + RemoveAt(j); + Add(trigger); + foreach (var t in pushItems) + Add(t); + } + + /// Removes the first occurrence of a specific object from the . + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method + /// also returns false if is not found in the original . + /// + public bool Remove([NotNull] Trigger item) + { + var idx = IndexOf(item); + if (idx != -1) + { + try + { + RemoveAt(idx); + return true; + } + catch { } + } + return false; + } + + /// Removes the trigger at a specified index. + /// Index of trigger to remove. + /// Index out of range. + public void RemoveAt(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException(nameof(index), index, @"Failed to remove Trigger. Index out of range."); + var item = this[index].Clone(); + if (v2Coll != null) + v2Coll.Remove(++index); + else + v1Task.DeleteTrigger((ushort)index); //Remove the trigger from the Task Scheduler + if (!inV2set) + { + OnNotifyPropertyChanged(nameof(Count)); + OnNotifyPropertyChanged(IndexerName); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); + } + } + + /// Copies the elements of the to a new array. + /// An array containing copies of the elements of the . + public Trigger[] ToArray() + { + var ret = new Trigger[Count]; + CopyTo(ret, 0); + return ret; + } + + /// Returns a that represents the triggers in this collection. + /// A that represents the triggers in this collection. + public override string ToString() + { + if (Count == 1) + return this[0].ToString(); + if (Count > 1) + return Properties.Resources.MultipleTriggers; + return string.Empty; + } + + void ICollection.Add(Trigger item) => Add(item); + + int IList.Add(object value) + { + Add((Trigger)value); + return Count - 1; + } + + bool IList.Contains(object value) => Contains((Trigger)value); + + void ICollection.CopyTo(Array array, int index) + { + if (array != null && array.Rank != 1) + throw new RankException("Multi-dimensional arrays are not supported."); + var src = new Trigger[Count]; + CopyTo(src, 0); + Array.Copy(src, 0, array, index, Count); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null; + + int IList.IndexOf(object value) => IndexOf((Trigger)value); + + void IList.Insert(int index, object value) => Insert(index, (Trigger)value); + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) + { + reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns); + while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "BootTrigger": + XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Boot)); + break; + + case "IdleTrigger": + XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Idle)); + break; + + case "TimeTrigger": + XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Time)); + break; + + case "LogonTrigger": + XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Logon)); + break; + + case "CalendarTrigger": + Add(CalendarTrigger.GetTriggerFromXml(reader)); + break; + + default: + reader.Skip(); + break; + } + } + reader.ReadEndElement(); + } + + void IList.Remove(object value) => Remove((Trigger)value); + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) + { + foreach (var t in this) + XmlSerializationHelper.WriteObject(writer, t); + } + + internal void Bind() + { + foreach (var t in this) + t.SetV1TriggerData(); + } + + /// Called when a property has changed to notify any attached elements. + /// Name of the property. + private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private sealed class V1TriggerEnumerator : IEnumerator + { + private short curItem = -1; + private ITask iTask; + + internal V1TriggerEnumerator(ITask task) => iTask = task; + + public Trigger Current => Trigger.CreateTrigger(iTask.GetTrigger((ushort)curItem)); + + object IEnumerator.Current => Current; + + /// Releases all resources used by this class. + public void Dispose() => iTask = null; + + public bool MoveNext() => (++curItem < iTask.GetTriggerCount()); + + public void Reset() => curItem = -1; + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/User.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/User.cs index 60428059f..230656daa 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/User.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/User.cs @@ -1,151 +1,147 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Security.Principal; -using System.Text; -using System.Threading.Tasks; using winPEAS.TaskScheduler.TaskEditor.Native; namespace winPEAS.TaskScheduler { - /// Represents a system account. - internal class User : IEquatable, IDisposable - { - private static readonly WindowsIdentity cur = WindowsIdentity.GetCurrent(); - private SecurityIdentifier sid; - - /// Initializes a new instance of the class. - /// - /// Name of the user. This can be in the format DOMAIN\username or username@domain.com or username or - /// null (for current user). - /// - public User(string userName = null) - { - if (string.IsNullOrEmpty(userName)) userName = null; - // 2018-03-02: Hopefully not a breaking change, but by adding in the comparison of an account name without a domain and the - // current user, there is a chance that current implementations will break given the condition that a local account with the same - // name as a domain account exists and the intention was to prefer the local account. In such a case, the developer should - // prepend the user name in TaskDefinition.Principal.UserId with the machine name of the local machine. - if (userName == null || cur.Name.Equals(userName, StringComparison.InvariantCultureIgnoreCase) || GetUser(cur.Name).Equals(userName, StringComparison.InvariantCultureIgnoreCase)) - { - Identity = cur; - sid = Identity.User; - } - else if (userName.Contains("\\") && !userName.StartsWith(@"NT AUTHORITY\")) - { - try - { - using (var ds = new NativeMethods.DomainService()) - { - Identity = new WindowsIdentity(ds.CrackName(userName)); - sid = Identity.User; - } - } - catch { } - } - - if (Identity == null) - { - if (userName != null && userName.Contains("@")) - { - Identity = new WindowsIdentity(userName); - sid = Identity.User; - } - - if (Identity == null && userName != null) - { - var ntacct = new NTAccount(userName); - try { sid = (SecurityIdentifier)ntacct.Translate(typeof(SecurityIdentifier)); } catch { } - } - } - - string GetUser(string domUser) - { - var split = domUser.Split('\\'); - return split.Length == 2 ? split[1] : domUser; - } - } - - /// Initializes a new instance of the class. - /// The . - internal User(WindowsIdentity wid) { Identity = wid; sid = wid.User; } - - /// Gets the current user. - /// The current user. - public static User Current => new User(cur); - - /// Gets the identity. - /// The identity. - public WindowsIdentity Identity { get; private set; } - - /// Gets a value indicating whether this instance is in an administrator role. - /// true if this instance is an admin; otherwise, false. - public bool IsAdmin => Identity != null ? new WindowsPrincipal(Identity).IsInRole(WindowsBuiltInRole.Administrator) : false; - - /// Gets a value indicating whether this instance is the interactive user. - /// true if this instance is the current user; otherwise, false. - public bool IsCurrent => Identity?.User.Equals(cur.User) ?? false; - - /// Gets a value indicating whether this instance is a service account. - /// true if this instance is a service account; otherwise, false. - public bool IsServiceAccount - { - get - { - try - { - return (sid != null && (sid.IsWellKnown(WellKnownSidType.LocalSystemSid) || sid.IsWellKnown(WellKnownSidType.NetworkServiceSid) || sid.IsWellKnown(WellKnownSidType.LocalServiceSid))); - } - catch { } - return false; - } - } - - /// Gets a value indicating whether this instance is the SYSTEM account. - /// true if this instance is the SYSTEM account; otherwise, false. - public bool IsSystem => sid != null && sid.IsWellKnown(WellKnownSidType.LocalSystemSid); - - /// Gets the SID string. - /// The SID string. - public string SidString => sid?.ToString(); - - /// Gets the NT name (DOMAIN\username). - /// The name of the user. - public string Name => Identity?.Name ?? ((NTAccount)sid?.Translate(typeof(NTAccount)))?.Value; - - /// Create a instance from a SID string. - /// The SID string. - /// A instance. - public static User FromSidString(string sid) => new User(((NTAccount)new SecurityIdentifier(sid).Translate(typeof(NTAccount))).Value); - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() => Identity?.Dispose(); - - /// Determines whether the specified , is equal to this instance. - /// The to compare with this instance. - /// true if the specified is equal to this instance; otherwise, false. - public override bool Equals(object obj) - { - if (obj is User user) - return Equals(user); - if (obj is WindowsIdentity wid && sid != null) - return sid.Equals(wid.User); - try - { - if (obj is string un) - return Equals(new User(un)); - } - catch { } - return base.Equals(obj); - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public bool Equals(User other) => (other != null && sid != null) ? sid.Equals(other.sid) : false; - - /// Returns a hash code for this instance. - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() => sid?.GetHashCode() ?? 0; - } + /// Represents a system account. + internal class User : IEquatable, IDisposable + { + private static readonly WindowsIdentity cur = WindowsIdentity.GetCurrent(); + private SecurityIdentifier sid; + + /// Initializes a new instance of the class. + /// + /// Name of the user. This can be in the format DOMAIN\username or username@domain.com or username or + /// null (for current user). + /// + public User(string userName = null) + { + if (string.IsNullOrEmpty(userName)) userName = null; + // 2018-03-02: Hopefully not a breaking change, but by adding in the comparison of an account name without a domain and the + // current user, there is a chance that current implementations will break given the condition that a local account with the same + // name as a domain account exists and the intention was to prefer the local account. In such a case, the developer should + // prepend the user name in TaskDefinition.Principal.UserId with the machine name of the local machine. + if (userName == null || cur.Name.Equals(userName, StringComparison.InvariantCultureIgnoreCase) || GetUser(cur.Name).Equals(userName, StringComparison.InvariantCultureIgnoreCase)) + { + Identity = cur; + sid = Identity.User; + } + else if (userName.Contains("\\") && !userName.StartsWith(@"NT AUTHORITY\")) + { + try + { + using (var ds = new NativeMethods.DomainService()) + { + Identity = new WindowsIdentity(ds.CrackName(userName)); + sid = Identity.User; + } + } + catch { } + } + + if (Identity == null) + { + if (userName != null && userName.Contains("@")) + { + Identity = new WindowsIdentity(userName); + sid = Identity.User; + } + + if (Identity == null && userName != null) + { + var ntacct = new NTAccount(userName); + try { sid = (SecurityIdentifier)ntacct.Translate(typeof(SecurityIdentifier)); } catch { } + } + } + + string GetUser(string domUser) + { + var split = domUser.Split('\\'); + return split.Length == 2 ? split[1] : domUser; + } + } + + /// Initializes a new instance of the class. + /// The . + internal User(WindowsIdentity wid) { Identity = wid; sid = wid.User; } + + /// Gets the current user. + /// The current user. + public static User Current => new User(cur); + + /// Gets the identity. + /// The identity. + public WindowsIdentity Identity { get; private set; } + + /// Gets a value indicating whether this instance is in an administrator role. + /// true if this instance is an admin; otherwise, false. + public bool IsAdmin => Identity != null ? new WindowsPrincipal(Identity).IsInRole(WindowsBuiltInRole.Administrator) : false; + + /// Gets a value indicating whether this instance is the interactive user. + /// true if this instance is the current user; otherwise, false. + public bool IsCurrent => Identity?.User.Equals(cur.User) ?? false; + + /// Gets a value indicating whether this instance is a service account. + /// true if this instance is a service account; otherwise, false. + public bool IsServiceAccount + { + get + { + try + { + return (sid != null && (sid.IsWellKnown(WellKnownSidType.LocalSystemSid) || sid.IsWellKnown(WellKnownSidType.NetworkServiceSid) || sid.IsWellKnown(WellKnownSidType.LocalServiceSid))); + } + catch { } + return false; + } + } + + /// Gets a value indicating whether this instance is the SYSTEM account. + /// true if this instance is the SYSTEM account; otherwise, false. + public bool IsSystem => sid != null && sid.IsWellKnown(WellKnownSidType.LocalSystemSid); + + /// Gets the SID string. + /// The SID string. + public string SidString => sid?.ToString(); + + /// Gets the NT name (DOMAIN\username). + /// The name of the user. + public string Name => Identity?.Name ?? ((NTAccount)sid?.Translate(typeof(NTAccount)))?.Value; + + /// Create a instance from a SID string. + /// The SID string. + /// A instance. + public static User FromSidString(string sid) => new User(((NTAccount)new SecurityIdentifier(sid).Translate(typeof(NTAccount))).Value); + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public void Dispose() => Identity?.Dispose(); + + /// Determines whether the specified , is equal to this instance. + /// The to compare with this instance. + /// true if the specified is equal to this instance; otherwise, false. + public override bool Equals(object obj) + { + if (obj is User user) + return Equals(user); + if (obj is WindowsIdentity wid && sid != null) + return sid.Equals(wid.User); + try + { + if (obj is string un) + return Equals(new User(un)); + } + catch { } + return base.Equals(obj); + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + public bool Equals(User other) => (other != null && sid != null) ? sid.Equals(other.sid) : false; + + /// Returns a hash code for this instance. + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + public override int GetHashCode() => sid?.GetHashCode() ?? 0; + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/V1/TaskSchedulerV1Interop.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/V1/TaskSchedulerV1Interop.cs index 39575fa25..2a2789aa9 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/V1/TaskSchedulerV1Interop.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/V1/TaskSchedulerV1Interop.cs @@ -6,340 +6,340 @@ namespace winPEAS.TaskScheduler.V1 { #pragma warning disable CS0618 // Type or member is obsolete - #region class HRESULT -- Values peculiar to the task scheduler. - internal class HResult - { - // The task is ready to run at its next scheduled time. - public const int SCHED_S_TASK_READY = 0x00041300; - // The task is currently running. - public const int SCHED_S_TASK_RUNNING = 0x00041301; - // The task will not run at the scheduled times because it has been disabled. - public const int SCHED_S_TASK_DISABLED = 0x00041302; - // The task has not yet run. - public const int SCHED_S_TASK_HAS_NOT_RUN = 0x00041303; - // There are no more runs scheduled for this task. - public const int SCHED_S_TASK_NO_MORE_RUNS = 0x00041304; - // One or more of the properties that are needed to run this task on a schedule have not been set. - public const int SCHED_S_TASK_NOT_SCHEDULED = 0x00041305; - // The last run of the task was terminated by the user. - public const int SCHED_S_TASK_TERMINATED = 0x00041306; - // Either the task has no triggers or the existing triggers are disabled or not set. - public const int SCHED_S_TASK_NO_VALID_TRIGGERS = 0x00041307; - // Event triggers don't have set run times. - public const int SCHED_S_EVENT_TRIGGER = 0x00041308; - // Trigger not found. - public const int SCHED_E_TRIGGER_NOT_FOUND = unchecked((int)0x80041309); - // One or more of the properties that are needed to run this task have not been set. - public const int SCHED_E_TASK_NOT_READY = unchecked((int)0x8004130A); - // There is no running instance of the task to terminate. - public const int SCHED_E_TASK_NOT_RUNNING = unchecked((int)0x8004130B); - // The Task Scheduler Service is not installed on this computer. - public const int SCHED_E_SERVICE_NOT_INSTALLED = unchecked((int)0x8004130C); - // The task object could not be opened. - public const int SCHED_E_CANNOT_OPEN_TASK = unchecked((int)0x8004130D); - // The object is either an invalid task object or is not a task object. - public const int SCHED_E_INVALID_TASK = unchecked((int)0x8004130E); - // No account information could be found in the Task Scheduler security database for the task indicated. - public const int SCHED_E_ACCOUNT_INFORMATION_NOT_SET = unchecked((int)0x8004130F); - // Unable to establish existence of the account specified. - public const int SCHED_E_ACCOUNT_NAME_NOT_FOUND = unchecked((int)0x80041310); - // Corruption was detected in the Task Scheduler security database; the database has been reset. - public const int SCHED_E_ACCOUNT_DBASE_CORRUPT = unchecked((int)0x80041311); - // Task Scheduler security services are available only on Windows NT. - public const int SCHED_E_NO_SECURITY_SERVICES = unchecked((int)0x80041312); - // The task object version is either unsupported or invalid. - public const int SCHED_E_UNKNOWN_OBJECT_VERSION = unchecked((int)0x80041313); - // The task has been configured with an unsupported combination of account settings and run time options. - public const int SCHED_E_UNSUPPORTED_ACCOUNT_OPTION = unchecked((int)0x80041314); - // The Task Scheduler Service is not running. - public const int SCHED_E_SERVICE_NOT_RUNNING = unchecked((int)0x80041315); - // The Task Scheduler service must be configured to run in the System account to function properly. Individual tasks may be configured to run in other accounts. - public const int SCHED_E_SERVICE_NOT_LOCALSYSTEM = unchecked((int)0x80041316); - } - #endregion - - #region Enums - - /// - /// Options for a task, used for the Flags property of a Task. Uses the - /// "Flags" attribute, so these values are combined with |. - /// Some flags are documented as Windows 95 only, but they have a - /// user interface in Windows XP so that may not be true. - /// - [Flags] - internal enum TaskFlags - { - /// - /// The interactive flag is set if the task is intended to be displayed to the user. - /// If the flag is not set, no user interface associated with the task is presented - /// to the user when the task is executed. - /// - Interactive = 0x1, - /// - /// The task will be deleted when there are no more scheduled run times. - /// - DeleteWhenDone = 0x2, - /// - /// The task is disabled. This is useful to temporarily prevent a task from running - /// at the scheduled time(s). - /// - Disabled = 0x4, - /// - /// The task begins only if the computer is not in use at the scheduled start time. Windows 95 only. - /// - StartOnlyIfIdle = 0x10, - /// - /// The task terminates if the computer makes an idle to non-idle transition while the task is running. - /// The computer is not considered idle until the IdleWait triggers' time elapses with no user input. - /// Windows 95 only. For information regarding idle triggers, see . - /// - KillOnIdleEnd = 0x20, - /// - /// The task does not start if its target computer is running on battery power. Windows 95 only. - /// - DontStartIfOnBatteries = 0x40, - /// - /// The task ends, and the associated application quits if the task's target computer switches - /// to battery power. Windows 95 only. - /// - KillIfGoingOnBatteries = 0x80, - /// - /// The task runs only if the system is docked. Windows 95 only. - /// - RunOnlyIfDocked = 0x100, - /// - /// The work item created will be hidden. - /// - Hidden = 0x200, - /// - /// The task runs only if there is currently a valid Internet connection. - /// This feature is currently not implemented. - /// - RunIfConnectedToInternet = 0x400, - /// - /// The task starts again if the computer makes a non-idle to idle transition before all the - /// task's task_triggers elapse. (Use this flag in conjunction with KillOnIdleEnd.) Windows 95 only. - /// - RestartOnIdleResume = 0x800, - /// - /// The task runs only if the SYSTEM account is available. - /// - SystemRequired = 0x1000, - /// - /// The task runs only if the user specified in SetAccountInformation is logged on interactively. - /// This flag has no effect on work items set to run in the local account. - /// - RunOnlyIfLoggedOn = 0x2000 - } - - /// - /// Status values returned for a task. Some values have been determined to occur although - /// they do no appear in the Task Scheduler system documentation. - /// - internal enum TaskStatus - { - /// The task is ready to run at its next scheduled time. - Ready = HResult.SCHED_S_TASK_READY, - /// The task is currently running. - Running = HResult.SCHED_S_TASK_RUNNING, - /// One or more of the properties that are needed to run this task on a schedule have not been set. - NotScheduled = HResult.SCHED_S_TASK_NOT_SCHEDULED, - /// The task has not yet run. - NeverRun = HResult.SCHED_S_TASK_HAS_NOT_RUN, - /// The task will not run at the scheduled times because it has been disabled. - Disabled = HResult.SCHED_S_TASK_DISABLED, - /// There are no more runs scheduled for this task. - NoMoreRuns = HResult.SCHED_S_TASK_NO_MORE_RUNS, - /// The last run of the task was terminated by the user. - Terminated = HResult.SCHED_S_TASK_TERMINATED, - /// Either the task has no triggers or the existing triggers are disabled or not set. - NoTriggers = HResult.SCHED_S_TASK_NO_VALID_TRIGGERS, - /// Event triggers don't have set run times. - NoTriggerTime = HResult.SCHED_S_EVENT_TRIGGER - } - - /// Valid types of triggers - internal enum TaskTriggerType - { - /// Trigger is set to run the task a single time. - RunOnce = 0, - /// Trigger is set to run the task on a daily interval. - RunDaily = 1, - /// Trigger is set to run the work item on specific days of a specific week of a specific month. - RunWeekly = 2, - /// Trigger is set to run the task on a specific day(s) of the month. - RunMonthly = 3, - /// Trigger is set to run the task on specific days, weeks, and months. - RunMonthlyDOW = 4, - /// Trigger is set to run the task if the system remains idle for the amount of time specified by the idle wait time of the task. - OnIdle = 5, - /// Trigger is set to run the task at system startup. - OnSystemStart = 6, - /// Trigger is set to run the task when a user logs on. - OnLogon = 7 - } - - [Flags] - internal enum TaskTriggerFlags : uint - { - HasEndDate = 0x1, - KillAtDurationEnd = 0x2, - Disabled = 0x4 - } - - #endregion - - #region Structs - - [StructLayout(LayoutKind.Sequential)] - internal struct Daily - { - public ushort DaysInterval; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct Weekly - { - public ushort WeeksInterval; - public DaysOfTheWeek DaysOfTheWeek; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MonthlyDate - { - public uint Days; - public MonthsOfTheYear Months; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MonthlyDOW - { - public ushort WhichWeek; - public DaysOfTheWeek DaysOfTheWeek; - public MonthsOfTheYear Months; - - public WhichWeek V2WhichWeek - { - get - { - return (WhichWeek)(1 << ((short)WhichWeek - 1)); - } - set - { - int idx = Array.IndexOf(new short[] { 0x1, 0x2, 0x4, 0x8, 0x10 }, (short)value); - if (idx >= 0) - WhichWeek = (ushort)(idx + 1); - else - throw new NotV1SupportedException("Only a single week can be set with Task Scheduler 1.0."); - } - } - } - - [StructLayout(LayoutKind.Explicit)] - internal struct TriggerTypeData - { - [FieldOffset(0)] - public Daily daily; - [FieldOffset(0)] - public Weekly weekly; - [FieldOffset(0)] - public MonthlyDate monthlyDate; - [FieldOffset(0)] - public MonthlyDOW monthlyDOW; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct TaskTrigger - { - public ushort TriggerSize; // Structure size. - public ushort Reserved1; // Reserved. Must be zero. - public ushort BeginYear; // Trigger beginning date year. - public ushort BeginMonth; // Trigger beginning date month. - public ushort BeginDay; // Trigger beginning date day. - public ushort EndYear; // Optional trigger ending date year. - public ushort EndMonth; // Optional trigger ending date month. - public ushort EndDay; // Optional trigger ending date day. - public ushort StartHour; // Run bracket start time hour. - public ushort StartMinute; // Run bracket start time minute. - public uint MinutesDuration; // Duration of run bracket. - public uint MinutesInterval; // Run bracket repetition interval. - public TaskTriggerFlags Flags; // Trigger flags. - public TaskTriggerType Type; // Trigger type. - public TriggerTypeData Data; // Trigger data peculiar to this type (union). - public ushort Reserved2; // Reserved. Must be zero. - public ushort RandomMinutesInterval; // Maximum number of random minutes after start time. - - public DateTime BeginDate - { - get { try { return BeginYear == 0 ? DateTime.MinValue : new DateTime(BeginYear, BeginMonth, BeginDay, StartHour, StartMinute, 0, DateTimeKind.Unspecified); } catch { return DateTime.MinValue; } } - set - { - if (value != DateTime.MinValue) - { - DateTime local = value.Kind == DateTimeKind.Utc ? value.ToLocalTime() : value; - BeginYear = (ushort)local.Year; - BeginMonth = (ushort)local.Month; - BeginDay = (ushort)local.Day; - StartHour = (ushort)local.Hour; - StartMinute = (ushort)local.Minute; - } - else - BeginYear = BeginMonth = BeginDay = StartHour = StartMinute = 0; - } - } - - public DateTime? EndDate - { - get { try { return EndYear == 0 ? (DateTime?)null : new DateTime(EndYear, EndMonth, EndDay); } catch { return DateTime.MaxValue; } } - set - { - if (value.HasValue) - { - EndYear = (ushort)value.Value.Year; - EndMonth = (ushort)value.Value.Month; - EndDay = (ushort)value.Value.Day; - Flags |= TaskTriggerFlags.HasEndDate; - } - else - { - EndYear = EndMonth = EndDay = 0; - Flags &= ~TaskTriggerFlags.HasEndDate; - } - } - } - - public override string ToString() => $"Trigger Type: {Type};\n> Start: {BeginDate}; End: {(EndYear == 0 ? "null" : EndDate?.ToString())};\n> DurMin: {MinutesDuration}; DurItv: {MinutesInterval};\n>"; - } - - #endregion - - [ComImport, Guid("148BD527-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTaskScheduler))] - internal interface ITaskScheduler - { - void SetTargetComputer([In, MarshalAs(UnmanagedType.LPWStr)] string Computer); - CoTaskMemString GetTargetComputer(); - [return: MarshalAs(UnmanagedType.Interface)] - IEnumWorkItems Enum(); - [return: MarshalAs(UnmanagedType.Interface)] - ITask Activate([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); - void Delete([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name); - [return: MarshalAs(UnmanagedType.Interface)] - ITask NewWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); - void AddWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.Interface)] ITask WorkItem); - void IsOfType([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); - } - - [Guid("148BD528-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IEnumWorkItems - { - [PreserveSig()] - //int Next([In] uint celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] out string[] rgpwszNames, [Out] out uint pceltFetched); - int Next([In] uint RequestCount, [Out] out IntPtr Names, [Out] out uint Fetched); - void Skip([In] uint Count); - void Reset(); - [return: MarshalAs(UnmanagedType.Interface)] - IEnumWorkItems Clone(); - } + #region class HRESULT -- Values peculiar to the task scheduler. + internal class HResult + { + // The task is ready to run at its next scheduled time. + public const int SCHED_S_TASK_READY = 0x00041300; + // The task is currently running. + public const int SCHED_S_TASK_RUNNING = 0x00041301; + // The task will not run at the scheduled times because it has been disabled. + public const int SCHED_S_TASK_DISABLED = 0x00041302; + // The task has not yet run. + public const int SCHED_S_TASK_HAS_NOT_RUN = 0x00041303; + // There are no more runs scheduled for this task. + public const int SCHED_S_TASK_NO_MORE_RUNS = 0x00041304; + // One or more of the properties that are needed to run this task on a schedule have not been set. + public const int SCHED_S_TASK_NOT_SCHEDULED = 0x00041305; + // The last run of the task was terminated by the user. + public const int SCHED_S_TASK_TERMINATED = 0x00041306; + // Either the task has no triggers or the existing triggers are disabled or not set. + public const int SCHED_S_TASK_NO_VALID_TRIGGERS = 0x00041307; + // Event triggers don't have set run times. + public const int SCHED_S_EVENT_TRIGGER = 0x00041308; + // Trigger not found. + public const int SCHED_E_TRIGGER_NOT_FOUND = unchecked((int)0x80041309); + // One or more of the properties that are needed to run this task have not been set. + public const int SCHED_E_TASK_NOT_READY = unchecked((int)0x8004130A); + // There is no running instance of the task to terminate. + public const int SCHED_E_TASK_NOT_RUNNING = unchecked((int)0x8004130B); + // The Task Scheduler Service is not installed on this computer. + public const int SCHED_E_SERVICE_NOT_INSTALLED = unchecked((int)0x8004130C); + // The task object could not be opened. + public const int SCHED_E_CANNOT_OPEN_TASK = unchecked((int)0x8004130D); + // The object is either an invalid task object or is not a task object. + public const int SCHED_E_INVALID_TASK = unchecked((int)0x8004130E); + // No account information could be found in the Task Scheduler security database for the task indicated. + public const int SCHED_E_ACCOUNT_INFORMATION_NOT_SET = unchecked((int)0x8004130F); + // Unable to establish existence of the account specified. + public const int SCHED_E_ACCOUNT_NAME_NOT_FOUND = unchecked((int)0x80041310); + // Corruption was detected in the Task Scheduler security database; the database has been reset. + public const int SCHED_E_ACCOUNT_DBASE_CORRUPT = unchecked((int)0x80041311); + // Task Scheduler security services are available only on Windows NT. + public const int SCHED_E_NO_SECURITY_SERVICES = unchecked((int)0x80041312); + // The task object version is either unsupported or invalid. + public const int SCHED_E_UNKNOWN_OBJECT_VERSION = unchecked((int)0x80041313); + // The task has been configured with an unsupported combination of account settings and run time options. + public const int SCHED_E_UNSUPPORTED_ACCOUNT_OPTION = unchecked((int)0x80041314); + // The Task Scheduler Service is not running. + public const int SCHED_E_SERVICE_NOT_RUNNING = unchecked((int)0x80041315); + // The Task Scheduler service must be configured to run in the System account to function properly. Individual tasks may be configured to run in other accounts. + public const int SCHED_E_SERVICE_NOT_LOCALSYSTEM = unchecked((int)0x80041316); + } + #endregion + + #region Enums + + /// + /// Options for a task, used for the Flags property of a Task. Uses the + /// "Flags" attribute, so these values are combined with |. + /// Some flags are documented as Windows 95 only, but they have a + /// user interface in Windows XP so that may not be true. + /// + [Flags] + internal enum TaskFlags + { + /// + /// The interactive flag is set if the task is intended to be displayed to the user. + /// If the flag is not set, no user interface associated with the task is presented + /// to the user when the task is executed. + /// + Interactive = 0x1, + /// + /// The task will be deleted when there are no more scheduled run times. + /// + DeleteWhenDone = 0x2, + /// + /// The task is disabled. This is useful to temporarily prevent a task from running + /// at the scheduled time(s). + /// + Disabled = 0x4, + /// + /// The task begins only if the computer is not in use at the scheduled start time. Windows 95 only. + /// + StartOnlyIfIdle = 0x10, + /// + /// The task terminates if the computer makes an idle to non-idle transition while the task is running. + /// The computer is not considered idle until the IdleWait triggers' time elapses with no user input. + /// Windows 95 only. For information regarding idle triggers, see . + /// + KillOnIdleEnd = 0x20, + /// + /// The task does not start if its target computer is running on battery power. Windows 95 only. + /// + DontStartIfOnBatteries = 0x40, + /// + /// The task ends, and the associated application quits if the task's target computer switches + /// to battery power. Windows 95 only. + /// + KillIfGoingOnBatteries = 0x80, + /// + /// The task runs only if the system is docked. Windows 95 only. + /// + RunOnlyIfDocked = 0x100, + /// + /// The work item created will be hidden. + /// + Hidden = 0x200, + /// + /// The task runs only if there is currently a valid Internet connection. + /// This feature is currently not implemented. + /// + RunIfConnectedToInternet = 0x400, + /// + /// The task starts again if the computer makes a non-idle to idle transition before all the + /// task's task_triggers elapse. (Use this flag in conjunction with KillOnIdleEnd.) Windows 95 only. + /// + RestartOnIdleResume = 0x800, + /// + /// The task runs only if the SYSTEM account is available. + /// + SystemRequired = 0x1000, + /// + /// The task runs only if the user specified in SetAccountInformation is logged on interactively. + /// This flag has no effect on work items set to run in the local account. + /// + RunOnlyIfLoggedOn = 0x2000 + } + + /// + /// Status values returned for a task. Some values have been determined to occur although + /// they do no appear in the Task Scheduler system documentation. + /// + internal enum TaskStatus + { + /// The task is ready to run at its next scheduled time. + Ready = HResult.SCHED_S_TASK_READY, + /// The task is currently running. + Running = HResult.SCHED_S_TASK_RUNNING, + /// One or more of the properties that are needed to run this task on a schedule have not been set. + NotScheduled = HResult.SCHED_S_TASK_NOT_SCHEDULED, + /// The task has not yet run. + NeverRun = HResult.SCHED_S_TASK_HAS_NOT_RUN, + /// The task will not run at the scheduled times because it has been disabled. + Disabled = HResult.SCHED_S_TASK_DISABLED, + /// There are no more runs scheduled for this task. + NoMoreRuns = HResult.SCHED_S_TASK_NO_MORE_RUNS, + /// The last run of the task was terminated by the user. + Terminated = HResult.SCHED_S_TASK_TERMINATED, + /// Either the task has no triggers or the existing triggers are disabled or not set. + NoTriggers = HResult.SCHED_S_TASK_NO_VALID_TRIGGERS, + /// Event triggers don't have set run times. + NoTriggerTime = HResult.SCHED_S_EVENT_TRIGGER + } + + /// Valid types of triggers + internal enum TaskTriggerType + { + /// Trigger is set to run the task a single time. + RunOnce = 0, + /// Trigger is set to run the task on a daily interval. + RunDaily = 1, + /// Trigger is set to run the work item on specific days of a specific week of a specific month. + RunWeekly = 2, + /// Trigger is set to run the task on a specific day(s) of the month. + RunMonthly = 3, + /// Trigger is set to run the task on specific days, weeks, and months. + RunMonthlyDOW = 4, + /// Trigger is set to run the task if the system remains idle for the amount of time specified by the idle wait time of the task. + OnIdle = 5, + /// Trigger is set to run the task at system startup. + OnSystemStart = 6, + /// Trigger is set to run the task when a user logs on. + OnLogon = 7 + } + + [Flags] + internal enum TaskTriggerFlags : uint + { + HasEndDate = 0x1, + KillAtDurationEnd = 0x2, + Disabled = 0x4 + } + + #endregion + + #region Structs + + [StructLayout(LayoutKind.Sequential)] + internal struct Daily + { + public ushort DaysInterval; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Weekly + { + public ushort WeeksInterval; + public DaysOfTheWeek DaysOfTheWeek; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MonthlyDate + { + public uint Days; + public MonthsOfTheYear Months; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MonthlyDOW + { + public ushort WhichWeek; + public DaysOfTheWeek DaysOfTheWeek; + public MonthsOfTheYear Months; + + public WhichWeek V2WhichWeek + { + get + { + return (WhichWeek)(1 << ((short)WhichWeek - 1)); + } + set + { + int idx = Array.IndexOf(new short[] { 0x1, 0x2, 0x4, 0x8, 0x10 }, (short)value); + if (idx >= 0) + WhichWeek = (ushort)(idx + 1); + else + throw new NotV1SupportedException("Only a single week can be set with Task Scheduler 1.0."); + } + } + } + + [StructLayout(LayoutKind.Explicit)] + internal struct TriggerTypeData + { + [FieldOffset(0)] + public Daily daily; + [FieldOffset(0)] + public Weekly weekly; + [FieldOffset(0)] + public MonthlyDate monthlyDate; + [FieldOffset(0)] + public MonthlyDOW monthlyDOW; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TaskTrigger + { + public ushort TriggerSize; // Structure size. + public ushort Reserved1; // Reserved. Must be zero. + public ushort BeginYear; // Trigger beginning date year. + public ushort BeginMonth; // Trigger beginning date month. + public ushort BeginDay; // Trigger beginning date day. + public ushort EndYear; // Optional trigger ending date year. + public ushort EndMonth; // Optional trigger ending date month. + public ushort EndDay; // Optional trigger ending date day. + public ushort StartHour; // Run bracket start time hour. + public ushort StartMinute; // Run bracket start time minute. + public uint MinutesDuration; // Duration of run bracket. + public uint MinutesInterval; // Run bracket repetition interval. + public TaskTriggerFlags Flags; // Trigger flags. + public TaskTriggerType Type; // Trigger type. + public TriggerTypeData Data; // Trigger data peculiar to this type (union). + public ushort Reserved2; // Reserved. Must be zero. + public ushort RandomMinutesInterval; // Maximum number of random minutes after start time. + + public DateTime BeginDate + { + get { try { return BeginYear == 0 ? DateTime.MinValue : new DateTime(BeginYear, BeginMonth, BeginDay, StartHour, StartMinute, 0, DateTimeKind.Unspecified); } catch { return DateTime.MinValue; } } + set + { + if (value != DateTime.MinValue) + { + DateTime local = value.Kind == DateTimeKind.Utc ? value.ToLocalTime() : value; + BeginYear = (ushort)local.Year; + BeginMonth = (ushort)local.Month; + BeginDay = (ushort)local.Day; + StartHour = (ushort)local.Hour; + StartMinute = (ushort)local.Minute; + } + else + BeginYear = BeginMonth = BeginDay = StartHour = StartMinute = 0; + } + } + + public DateTime? EndDate + { + get { try { return EndYear == 0 ? (DateTime?)null : new DateTime(EndYear, EndMonth, EndDay); } catch { return DateTime.MaxValue; } } + set + { + if (value.HasValue) + { + EndYear = (ushort)value.Value.Year; + EndMonth = (ushort)value.Value.Month; + EndDay = (ushort)value.Value.Day; + Flags |= TaskTriggerFlags.HasEndDate; + } + else + { + EndYear = EndMonth = EndDay = 0; + Flags &= ~TaskTriggerFlags.HasEndDate; + } + } + } + + public override string ToString() => $"Trigger Type: {Type};\n> Start: {BeginDate}; End: {(EndYear == 0 ? "null" : EndDate?.ToString())};\n> DurMin: {MinutesDuration}; DurItv: {MinutesInterval};\n>"; + } + + #endregion + + [ComImport, Guid("148BD527-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTaskScheduler))] + internal interface ITaskScheduler + { + void SetTargetComputer([In, MarshalAs(UnmanagedType.LPWStr)] string Computer); + CoTaskMemString GetTargetComputer(); + [return: MarshalAs(UnmanagedType.Interface)] + IEnumWorkItems Enum(); + [return: MarshalAs(UnmanagedType.Interface)] + ITask Activate([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); + void Delete([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name); + [return: MarshalAs(UnmanagedType.Interface)] + ITask NewWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); + void AddWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.Interface)] ITask WorkItem); + void IsOfType([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); + } + + [Guid("148BD528-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IEnumWorkItems + { + [PreserveSig()] + //int Next([In] uint celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] out string[] rgpwszNames, [Out] out uint pceltFetched); + int Next([In] uint RequestCount, [Out] out IntPtr Names, [Out] out uint Fetched); + void Skip([In] uint Count); + void Reset(); + [return: MarshalAs(UnmanagedType.Interface)] + IEnumWorkItems Clone(); + } #if WorkItem // The IScheduledWorkItem interface is actually never used because ITask inherits all of its @@ -379,92 +379,92 @@ internal interface IScheduledWorkItem } #endif - [ComImport, Guid("148BD524-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTask))] - internal interface ITask - { - [return: MarshalAs(UnmanagedType.Interface)] - ITaskTrigger CreateTrigger([Out] out ushort NewTriggerIndex); - void DeleteTrigger([In] ushort TriggerIndex); - [return: MarshalAs(UnmanagedType.U2)] - ushort GetTriggerCount(); - [return: MarshalAs(UnmanagedType.Interface)] - ITaskTrigger GetTrigger([In] ushort TriggerIndex); - CoTaskMemString GetTriggerString([In] ushort TriggerIndex); - void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME Begin, [In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME End, ref ushort Count, [In, Out] ref IntPtr TaskTimes); - [return: MarshalAs(UnmanagedType.Struct)] - NativeMethods.SYSTEMTIME GetNextRunTime(); - void SetIdleWait([In] ushort IdleMinutes, [In] ushort DeadlineMinutes); - void GetIdleWait([Out] out ushort IdleMinutes, [Out] out ushort DeadlineMinutes); - void Run(); - void Terminate(); - void EditWorkItem([In] IntPtr hParent, [In] uint dwReserved); - [return: MarshalAs(UnmanagedType.Struct)] - NativeMethods.SYSTEMTIME GetMostRecentRunTime(); - TaskStatus GetStatus(); - uint GetExitCode(); - void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment); - CoTaskMemString GetComment(); - void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator); - CoTaskMemString GetCreator(); - void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U1)] byte[] Data); - void GetWorkItemData(out ushort DataLen, [Out] out IntPtr Data); - void SetErrorRetryCount([In] ushort RetryCount); - ushort GetErrorRetryCount(); - void SetErrorRetryInterval([In] ushort RetryInterval); - ushort GetErrorRetryInterval(); - void SetFlags([In] TaskFlags Flags); - TaskFlags GetFlags(); - void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In] IntPtr Password); - CoTaskMemString GetAccountInformation(); - void SetApplicationName([In, MarshalAs(UnmanagedType.LPWStr)] string ApplicationName); - CoTaskMemString GetApplicationName(); - void SetParameters([In, MarshalAs(UnmanagedType.LPWStr)] string Parameters); - CoTaskMemString GetParameters(); - void SetWorkingDirectory([In, MarshalAs(UnmanagedType.LPWStr)] string WorkingDirectory); - CoTaskMemString GetWorkingDirectory(); - void SetPriority([In] uint Priority); - uint GetPriority(); - void SetTaskFlags([In] uint Flags); - uint GetTaskFlags(); - void SetMaxRunTime([In] uint MaxRunTimeMS); - uint GetMaxRunTime(); - } - - [Guid("148BD52B-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskTrigger - { - void SetTrigger([In, Out, MarshalAs(UnmanagedType.Struct)] ref TaskTrigger Trigger); - [return: MarshalAs(UnmanagedType.Struct)] - TaskTrigger GetTrigger(); - CoTaskMemString GetTriggerString(); - } - - [ComImport, Guid("148BD52A-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)] - internal class CTaskScheduler - { - } - - [ComImport, Guid("148BD520-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)] - internal class CTask - { - } - - internal sealed class CoTaskMemString : SafeHandle - { - public CoTaskMemString() : base(IntPtr.Zero, true) { } - public CoTaskMemString(IntPtr handle) : this() { SetHandle(handle); } - public CoTaskMemString(string text) : this() { SetHandle(Marshal.StringToCoTaskMemUni(text)); } - - public static implicit operator string(CoTaskMemString cmem) => cmem.ToString(); - - public override bool IsInvalid => handle == IntPtr.Zero; - - protected override bool ReleaseHandle() - { - Marshal.FreeCoTaskMem(handle); - return true; - } - - public override string ToString() => Marshal.PtrToStringUni(handle); - } + [ComImport, Guid("148BD524-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTask))] + internal interface ITask + { + [return: MarshalAs(UnmanagedType.Interface)] + ITaskTrigger CreateTrigger([Out] out ushort NewTriggerIndex); + void DeleteTrigger([In] ushort TriggerIndex); + [return: MarshalAs(UnmanagedType.U2)] + ushort GetTriggerCount(); + [return: MarshalAs(UnmanagedType.Interface)] + ITaskTrigger GetTrigger([In] ushort TriggerIndex); + CoTaskMemString GetTriggerString([In] ushort TriggerIndex); + void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME Begin, [In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME End, ref ushort Count, [In, Out] ref IntPtr TaskTimes); + [return: MarshalAs(UnmanagedType.Struct)] + NativeMethods.SYSTEMTIME GetNextRunTime(); + void SetIdleWait([In] ushort IdleMinutes, [In] ushort DeadlineMinutes); + void GetIdleWait([Out] out ushort IdleMinutes, [Out] out ushort DeadlineMinutes); + void Run(); + void Terminate(); + void EditWorkItem([In] IntPtr hParent, [In] uint dwReserved); + [return: MarshalAs(UnmanagedType.Struct)] + NativeMethods.SYSTEMTIME GetMostRecentRunTime(); + TaskStatus GetStatus(); + uint GetExitCode(); + void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment); + CoTaskMemString GetComment(); + void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator); + CoTaskMemString GetCreator(); + void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U1)] byte[] Data); + void GetWorkItemData(out ushort DataLen, [Out] out IntPtr Data); + void SetErrorRetryCount([In] ushort RetryCount); + ushort GetErrorRetryCount(); + void SetErrorRetryInterval([In] ushort RetryInterval); + ushort GetErrorRetryInterval(); + void SetFlags([In] TaskFlags Flags); + TaskFlags GetFlags(); + void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In] IntPtr Password); + CoTaskMemString GetAccountInformation(); + void SetApplicationName([In, MarshalAs(UnmanagedType.LPWStr)] string ApplicationName); + CoTaskMemString GetApplicationName(); + void SetParameters([In, MarshalAs(UnmanagedType.LPWStr)] string Parameters); + CoTaskMemString GetParameters(); + void SetWorkingDirectory([In, MarshalAs(UnmanagedType.LPWStr)] string WorkingDirectory); + CoTaskMemString GetWorkingDirectory(); + void SetPriority([In] uint Priority); + uint GetPriority(); + void SetTaskFlags([In] uint Flags); + uint GetTaskFlags(); + void SetMaxRunTime([In] uint MaxRunTimeMS); + uint GetMaxRunTime(); + } + + [Guid("148BD52B-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskTrigger + { + void SetTrigger([In, Out, MarshalAs(UnmanagedType.Struct)] ref TaskTrigger Trigger); + [return: MarshalAs(UnmanagedType.Struct)] + TaskTrigger GetTrigger(); + CoTaskMemString GetTriggerString(); + } + + [ComImport, Guid("148BD52A-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)] + internal class CTaskScheduler + { + } + + [ComImport, Guid("148BD520-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)] + internal class CTask + { + } + + internal sealed class CoTaskMemString : SafeHandle + { + public CoTaskMemString() : base(IntPtr.Zero, true) { } + public CoTaskMemString(IntPtr handle) : this() { SetHandle(handle); } + public CoTaskMemString(string text) : this() { SetHandle(Marshal.StringToCoTaskMemUni(text)); } + + public static implicit operator string(CoTaskMemString cmem) => cmem.ToString(); + + public override bool IsInvalid => handle == IntPtr.Zero; + + protected override bool ReleaseHandle() + { + Marshal.FreeCoTaskMem(handle); + return true; + } + + public override string ToString() => Marshal.PtrToStringUni(handle); + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/V2/TaskSchedulerV2Interop.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/V2/TaskSchedulerV2Interop.cs index 6c39ac1d1..ed7d891b7 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/V2/TaskSchedulerV2Interop.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/V2/TaskSchedulerV2Interop.cs @@ -8,570 +8,570 @@ namespace winPEAS.TaskScheduler.V2 { - internal enum TaskEnumFlags - { - Hidden = 1 - } + internal enum TaskEnumFlags + { + Hidden = 1 + } #pragma warning disable CS0618 // Type or member is obsolete - [ComImport, Guid("BAE54997-48B1-4CBE-9965-D6BE263EBEA4"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IAction - { - string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - TaskActionType Type { get; } - } - - [ComImport, Guid("02820E19-7B98-4ED2-B2E8-FDCCCEFF619B"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IActionCollection - { - int Count { get; } - IAction this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - IEnumerator GetEnumerator(); - string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - [return: MarshalAs(UnmanagedType.Interface)] - IAction Create([In] TaskActionType Type); - void Remove([In, MarshalAs(UnmanagedType.Struct)][NotNull] object index); - void Clear(); - string Context { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("2A9C35DA-D357-41F4-BBC1-207AC1B1F3CB"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IBootTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("6D2FD252-75C5-4F66-90BA-2A7D8CC3039F"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IComHandlerAction : IAction - { - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new TaskActionType Type { get; } - string ClassId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Data { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("126C5CD8-B288-41D5-8DBF-E491446ADC5C"), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IDailyTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - short DaysInterval { get; [param: In] set; } - string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("10F62C64-7E16-4314-A0C2-0C3683F99D40"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IEmailAction : IAction - { - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new TaskActionType Type { get; } - - string Server { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Subject { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string To { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Cc { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Bcc { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string ReplyTo { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string From { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - ITaskNamedValueCollection HeaderFields { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - string Body { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - object[] Attachments { [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] get; [param: In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] set; } - } - - [ComImport, Guid("D45B0167-9653-4EEF-B94F-0732CA7AF251"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IEventTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - string Subscription { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - ITaskNamedValueCollection ValueQueries { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - } - - [ComImport, Guid("4C3D624D-FD6B-49A3-B9B7-09CB3CD3F047"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IExecAction : IAction - { - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new TaskActionType Type { get; } - - string Path { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Arguments { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("84594461-0053-4342-A8FD-088FABF11F32"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IIdleSettings - { - string IdleDuration { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string WaitTimeout { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - bool StopOnIdleEnd { get; [param: In] set; } - bool RestartOnIdle { get; [param: In] set; } - } - - [ComImport, Guid("D537D2B0-9FB3-4D34-9739-1FF5CE7B1EF3"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IIdleTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - } - - [ComImport, Guid("72DADE38-FAE4-4B3E-BAF4-5D009AF02B1C"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ILogonTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("77D025A3-90FA-43AA-B52E-CDA5499B946A"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IMonthlyDOWTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - short DaysOfWeek { get; [param: In] set; } - short WeeksOfMonth { get; [param: In] set; } - short MonthsOfYear { get; [param: In] set; } - bool RunOnLastWeekOfMonth { get; [param: In] set; } - string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("97C45EF1-6B02-4A1A-9C0E-1EBFBA1500AC"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IMonthlyTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - int DaysOfMonth { get; [param: In] set; } - short MonthsOfYear { get; [param: In] set; } - bool RunOnLastDayOfMonth { get; [param: In] set; } - string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("9F7DEA84-C30B-4245-80B6-00E9F646F1B4"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface INetworkSettings - { - string Name { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("D98D51E5-C9B4-496A-A9C1-18980261CF0F"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IPrincipal - { - string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string DisplayName { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - TaskLogonType LogonType { get; set; } - string GroupId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - TaskRunLevel RunLevel { get; set; } - } - - [ComImport, Guid("248919AE-E345-4A6D-8AEB-E0D3165C904E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IPrincipal2 - { - TaskProcessTokenSidType ProcessTokenSidType { get; [param: In] set; } - int RequiredPrivilegeCount { get; } - string this[int index] { [return: MarshalAs(UnmanagedType.BStr)] get; } - void AddRequiredPrivilege([In, MarshalAs(UnmanagedType.BStr)] string privilege); - } - - [ComImport, Guid("9C86F320-DEE3-4DD1-B972-A303F26B061E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Path")] - internal interface IRegisteredTask - { - string Name { [return: MarshalAs(UnmanagedType.BStr)] get; } - string Path { [return: MarshalAs(UnmanagedType.BStr)] get; } - TaskState State { get; } - bool Enabled { get; set; } - [return: MarshalAs(UnmanagedType.Interface)] - IRunningTask Run([In, MarshalAs(UnmanagedType.Struct)] object parameters); - [return: MarshalAs(UnmanagedType.Interface)] - IRunningTask RunEx([In, MarshalAs(UnmanagedType.Struct)] object parameters, [In] int flags, [In] int sessionID, [In, MarshalAs(UnmanagedType.BStr)] string user); - [return: MarshalAs(UnmanagedType.Interface)] - IRunningTaskCollection GetInstances(int flags); - DateTime LastRunTime { get; } - int LastTaskResult { get; } - int NumberOfMissedRuns { get; } - DateTime NextRunTime { get; } - ITaskDefinition Definition { [return: MarshalAs(UnmanagedType.Interface)] get; } - string Xml { [return: MarshalAs(UnmanagedType.BStr)] get; } - [return: MarshalAs(UnmanagedType.BStr)] - string GetSecurityDescriptor(int securityInformation); - void SetSecurityDescriptor([In, MarshalAs(UnmanagedType.BStr)] string sddl, [In] int flags); - void Stop(int flags); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x60020011)] - void GetRunTimes([In] ref NativeMethods.SYSTEMTIME pstStart, [In] ref NativeMethods.SYSTEMTIME pstEnd, [In, Out] ref uint pCount, [In, Out] ref IntPtr pRunTimes); - } - - [ComImport, Guid("86627EB4-42A7-41E4-A4D9-AC33A72F2D52"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IRegisteredTaskCollection - { - int Count { get; } - IRegisteredTask this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - IEnumerator GetEnumerator(); - } - - [ComImport, Guid("416D8B73-CB41-4EA1-805C-9BE9A5AC4A74"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IRegistrationInfo - { - string Description { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Author { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Version { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Date { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Documentation { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string URI { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - object SecurityDescriptor { [return: MarshalAs(UnmanagedType.Struct)] get; [param: In, MarshalAs(UnmanagedType.Struct)] set; } - string Source { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("4C8FEC3A-C218-4E0C-B23D-629024DB91A2"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IRegistrationTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("7FB9ACF1-26BE-400E-85B5-294B9C75DFD6"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IRepetitionPattern - { - string Interval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Duration { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - bool StopAtDurationEnd { get; [param: In] set; } - } - - [ComImport, Guid("653758FB-7B9A-4F1E-A471-BEEB8E9B834E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("InstanceGuid")] - internal interface IRunningTask - { - string Name { [return: MarshalAs(UnmanagedType.BStr)] get; } - string InstanceGuid { [return: MarshalAs(UnmanagedType.BStr)] get; } - string Path { [return: MarshalAs(UnmanagedType.BStr)] get; } - TaskState State { get; } - string CurrentAction { [return: MarshalAs(UnmanagedType.BStr)] get; } - void Stop(); - void Refresh(); - uint EnginePID { get; } - } - - [ComImport, Guid("6A67614B-6828-4FEC-AA54-6D52E8F1F2DB"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IRunningTaskCollection - { - int Count { get; } - IRunningTask this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - IEnumerator GetEnumerator(); - } - - [ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("754DA71B-4385-4475-9DD9-598294FA3641"), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ISessionStateChangeTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - TaskSessionStateChangeType StateChange { get; [param: In] set; } - } - - [ComImport, Guid("505E9E68-AF89-46B8-A30F-56162A83D537"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IShowMessageAction : IAction - { - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new TaskActionType Type { get; } - - string Title { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string MessageBody { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("F5BC8FC5-536D-4F77-B852-FBC1356FDEB6"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskDefinition - { - IRegistrationInfo RegistrationInfo { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - ITriggerCollection Triggers { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - ITaskSettings Settings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - string Data { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - IPrincipal Principal { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - IActionCollection Actions { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("8CFAC062-A080-4C15-9A88-AA7C2AF80DFC"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Path")] - internal interface ITaskFolder - { - string Name { [return: MarshalAs(UnmanagedType.BStr)] get; } - string Path { [return: MarshalAs(UnmanagedType.BStr)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - ITaskFolder GetFolder([MarshalAs(UnmanagedType.BStr)] string Path); - [return: MarshalAs(UnmanagedType.Interface)] - ITaskFolderCollection GetFolders(int flags); - [return: MarshalAs(UnmanagedType.Interface)] - ITaskFolder CreateFolder([In, MarshalAs(UnmanagedType.BStr)] string subFolderName, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl); - void DeleteFolder([MarshalAs(UnmanagedType.BStr)] string subFolderName, [In] int flags); - [return: MarshalAs(UnmanagedType.Interface)] - IRegisteredTask GetTask([MarshalAs(UnmanagedType.BStr)][NotNull] string Path); - [return: MarshalAs(UnmanagedType.Interface)] - IRegisteredTaskCollection GetTasks(int flags); - void DeleteTask([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Name, [In] int flags); - [return: MarshalAs(UnmanagedType.Interface)] - IRegisteredTask RegisterTask([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path, [In, MarshalAs(UnmanagedType.BStr)][NotNull] string XmlText, [In] int flags, [In, MarshalAs(UnmanagedType.Struct)] object UserId, [In, MarshalAs(UnmanagedType.Struct)] object password, [In] TaskLogonType LogonType, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl); - [return: MarshalAs(UnmanagedType.Interface)] - IRegisteredTask RegisterTaskDefinition([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path, [In, MarshalAs(UnmanagedType.Interface)][NotNull] ITaskDefinition pDefinition, [In] int flags, [In, MarshalAs(UnmanagedType.Struct)] object UserId, [In, MarshalAs(UnmanagedType.Struct)] object password, [In] TaskLogonType LogonType, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl); - [return: MarshalAs(UnmanagedType.BStr)] - string GetSecurityDescriptor(int securityInformation); - void SetSecurityDescriptor([In, MarshalAs(UnmanagedType.BStr)] string sddl, [In] int flags); - } - - [ComImport, Guid("79184A66-8664-423F-97F1-637356A5D812"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskFolderCollection - { - int Count { get; } - ITaskFolder this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - IEnumerator GetEnumerator(); - } - - [ComImport, Guid("B4EF826B-63C3-46E4-A504-EF69E4F7EA4D"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskNamedValueCollection - { - int Count { get; } - ITaskNamedValuePair this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - IEnumerator GetEnumerator(); - [return: MarshalAs(UnmanagedType.Interface)] - ITaskNamedValuePair Create([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.BStr)] string Value); - void Remove([In] int index); - void Clear(); - } - - [ComImport, Guid("39038068-2B46-4AFD-8662-7BB6F868D221"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Name")] - internal interface ITaskNamedValuePair - { - string Name { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string Value { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, DefaultMember("TargetServer"), Guid("2FABA4C7-4DA9-4013-9697-20CC3FD40F85"), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(TaskSchedulerClass))] - internal interface ITaskService - { - [return: MarshalAs(UnmanagedType.Interface)] - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] - ITaskFolder GetFolder([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path); - [return: MarshalAs(UnmanagedType.Interface)] - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] - IRunningTaskCollection GetRunningTasks(int flags); - [return: MarshalAs(UnmanagedType.Interface)] - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] - ITaskDefinition NewTask([In] uint flags); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)] - void Connect([In, Optional, MarshalAs(UnmanagedType.Struct)] object serverName, [In, Optional, MarshalAs(UnmanagedType.Struct)] object user, [In, Optional, MarshalAs(UnmanagedType.Struct)] object domain, [In, Optional, MarshalAs(UnmanagedType.Struct)] object password); - [DispId(5)] - bool Connected { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(5)] get; } - [DispId(0)] - string TargetServer { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0)] get; } - [DispId(6)] - string ConnectedUser { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(6)] get; } - [DispId(7)] - string ConnectedDomain { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(7)] get; } - [DispId(8)] - uint HighestVersion { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(8)] get; } - } - - [ComImport, DefaultMember("TargetServer"), Guid("0F87369F-A4E5-4CFC-BD3E-73E6154572DD"), ClassInterface((short)0), System.Security.SuppressUnmanagedCodeSecurity] - internal class TaskSchedulerClass - { - } - - [ComImport, Guid("8FD4711D-2D02-4C8C-87E3-EFF699DE127E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskSettings - { - bool AllowDemandStart { get; [param: In] set; } - string RestartInterval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - int RestartCount { get; [param: In] set; } - TaskInstancesPolicy MultipleInstances { get; [param: In] set; } - bool StopIfGoingOnBatteries { get; [param: In] set; } - bool DisallowStartIfOnBatteries { get; [param: In] set; } - bool AllowHardTerminate { get; [param: In] set; } - bool StartWhenAvailable { get; [param: In] set; } - string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - bool RunOnlyIfNetworkAvailable { get; [param: In] set; } - string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - bool Enabled { get; [param: In] set; } - string DeleteExpiredTaskAfter { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - int Priority { get; [param: In] set; } - TaskCompatibility Compatibility { get; [param: In] set; } - bool Hidden { get; [param: In] set; } - IIdleSettings IdleSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - bool RunOnlyIfIdle { get; [param: In] set; } - bool WakeToRun { get; [param: In] set; } - INetworkSettings NetworkSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - } - - [ComImport, Guid("2C05C3F0-6EED-4c05-A15F-ED7D7A98A369"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskSettings2 - { - bool DisallowStartOnRemoteAppSession { get; [param: In] set; } - bool UseUnifiedSchedulingEngine { get; [param: In] set; } - } - - [ComImport, Guid("0AD9D0D7-0C7F-4EBB-9A5F-D1C648DCA528"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskSettings3 : ITaskSettings - { - new bool AllowDemandStart { get; [param: In] set; } - new string RestartInterval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new int RestartCount { get; [param: In] set; } - new TaskInstancesPolicy MultipleInstances { get; [param: In] set; } - new bool StopIfGoingOnBatteries { get; [param: In] set; } - new bool DisallowStartIfOnBatteries { get; [param: In] set; } - new bool AllowHardTerminate { get; [param: In] set; } - new bool StartWhenAvailable { get; [param: In] set; } - new string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool RunOnlyIfNetworkAvailable { get; [param: In] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - new string DeleteExpiredTaskAfter { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new int Priority { get; [param: In] set; } - new TaskCompatibility Compatibility { get; [param: In] set; } - new bool Hidden { get; [param: In] set; } - new IIdleSettings IdleSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new bool RunOnlyIfIdle { get; [param: In] set; } - new bool WakeToRun { get; [param: In] set; } - new INetworkSettings NetworkSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - - bool DisallowStartOnRemoteAppSession { get; [param: In] set; } - bool UseUnifiedSchedulingEngine { get; [param: In] set; } - IMaintenanceSettings MaintenanceSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - [return: MarshalAs(UnmanagedType.Interface)] - IMaintenanceSettings CreateMaintenanceSettings(); - bool Volatile { get; [param: In] set; } - } - - [ComImport, Guid("A6024FA8-9652-4ADB-A6BF-5CFCD877A7BA"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IMaintenanceSettings - { - string Period { [param: In, MarshalAs(UnmanagedType.BStr)] set; [return: MarshalAs(UnmanagedType.BStr)] get; } - string Deadline { [param: In, MarshalAs(UnmanagedType.BStr)] set; [return: MarshalAs(UnmanagedType.BStr)] get; } - bool Exclusive { [param: In] set; get; } - } - - [ComImport, Guid("3E4C9351-D966-4B8B-BB87-CEBA68BB0107"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITaskVariables - { - [return: MarshalAs(UnmanagedType.BStr)] - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - string GetInput(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetOutput([In, MarshalAs(UnmanagedType.BStr)] string input); - [return: MarshalAs(UnmanagedType.BStr)] - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - string GetContext(); - } - - [ComImport, Guid("B45747E0-EBA7-4276-9F29-85C5BB300006"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITimeTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } - - [ComImport, Guid("09941815-EA89-4B5B-89E0-2A773801FAC3"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITrigger - { - TaskTriggerType Type { get; } - string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - bool Enabled { get; [param: In] set; } - } - - [ComImport, Guid("85DF5081-1B24-4F32-878A-D9D14DF4CB77"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface ITriggerCollection - { - int Count { get; } - ITrigger this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; } - [return: MarshalAs(UnmanagedType.Interface)] - IEnumerator GetEnumerator(); - [return: MarshalAs(UnmanagedType.Interface)] - ITrigger Create([In] TaskTriggerType Type); - void Remove([In, MarshalAs(UnmanagedType.Struct)] object index); - void Clear(); - } - - [ComImport, Guid("5038FC98-82FF-436D-8728-A512A57C9DC1"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] - internal interface IWeeklyTrigger : ITrigger - { - new TaskTriggerType Type { get; } - new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } - new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - new bool Enabled { get; [param: In] set; } - - short DaysOfWeek { get; [param: In] set; } - short WeeksInterval { get; [param: In] set; } - string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } - } + [ComImport, Guid("BAE54997-48B1-4CBE-9965-D6BE263EBEA4"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IAction + { + string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + TaskActionType Type { get; } + } + + [ComImport, Guid("02820E19-7B98-4ED2-B2E8-FDCCCEFF619B"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IActionCollection + { + int Count { get; } + IAction this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + IEnumerator GetEnumerator(); + string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + [return: MarshalAs(UnmanagedType.Interface)] + IAction Create([In] TaskActionType Type); + void Remove([In, MarshalAs(UnmanagedType.Struct)][NotNull] object index); + void Clear(); + string Context { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("2A9C35DA-D357-41F4-BBC1-207AC1B1F3CB"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IBootTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("6D2FD252-75C5-4F66-90BA-2A7D8CC3039F"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IComHandlerAction : IAction + { + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new TaskActionType Type { get; } + string ClassId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Data { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("126C5CD8-B288-41D5-8DBF-E491446ADC5C"), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IDailyTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + short DaysInterval { get; [param: In] set; } + string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("10F62C64-7E16-4314-A0C2-0C3683F99D40"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IEmailAction : IAction + { + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new TaskActionType Type { get; } + + string Server { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Subject { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string To { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Cc { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Bcc { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string ReplyTo { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string From { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + ITaskNamedValueCollection HeaderFields { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + string Body { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + object[] Attachments { [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] get; [param: In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] set; } + } + + [ComImport, Guid("D45B0167-9653-4EEF-B94F-0732CA7AF251"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IEventTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + string Subscription { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + ITaskNamedValueCollection ValueQueries { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + } + + [ComImport, Guid("4C3D624D-FD6B-49A3-B9B7-09CB3CD3F047"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IExecAction : IAction + { + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new TaskActionType Type { get; } + + string Path { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Arguments { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("84594461-0053-4342-A8FD-088FABF11F32"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IIdleSettings + { + string IdleDuration { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string WaitTimeout { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + bool StopOnIdleEnd { get; [param: In] set; } + bool RestartOnIdle { get; [param: In] set; } + } + + [ComImport, Guid("D537D2B0-9FB3-4D34-9739-1FF5CE7B1EF3"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IIdleTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + } + + [ComImport, Guid("72DADE38-FAE4-4B3E-BAF4-5D009AF02B1C"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ILogonTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("77D025A3-90FA-43AA-B52E-CDA5499B946A"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IMonthlyDOWTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + short DaysOfWeek { get; [param: In] set; } + short WeeksOfMonth { get; [param: In] set; } + short MonthsOfYear { get; [param: In] set; } + bool RunOnLastWeekOfMonth { get; [param: In] set; } + string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("97C45EF1-6B02-4A1A-9C0E-1EBFBA1500AC"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IMonthlyTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + int DaysOfMonth { get; [param: In] set; } + short MonthsOfYear { get; [param: In] set; } + bool RunOnLastDayOfMonth { get; [param: In] set; } + string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("9F7DEA84-C30B-4245-80B6-00E9F646F1B4"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface INetworkSettings + { + string Name { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("D98D51E5-C9B4-496A-A9C1-18980261CF0F"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IPrincipal + { + string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string DisplayName { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + TaskLogonType LogonType { get; set; } + string GroupId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + TaskRunLevel RunLevel { get; set; } + } + + [ComImport, Guid("248919AE-E345-4A6D-8AEB-E0D3165C904E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IPrincipal2 + { + TaskProcessTokenSidType ProcessTokenSidType { get; [param: In] set; } + int RequiredPrivilegeCount { get; } + string this[int index] { [return: MarshalAs(UnmanagedType.BStr)] get; } + void AddRequiredPrivilege([In, MarshalAs(UnmanagedType.BStr)] string privilege); + } + + [ComImport, Guid("9C86F320-DEE3-4DD1-B972-A303F26B061E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Path")] + internal interface IRegisteredTask + { + string Name { [return: MarshalAs(UnmanagedType.BStr)] get; } + string Path { [return: MarshalAs(UnmanagedType.BStr)] get; } + TaskState State { get; } + bool Enabled { get; set; } + [return: MarshalAs(UnmanagedType.Interface)] + IRunningTask Run([In, MarshalAs(UnmanagedType.Struct)] object parameters); + [return: MarshalAs(UnmanagedType.Interface)] + IRunningTask RunEx([In, MarshalAs(UnmanagedType.Struct)] object parameters, [In] int flags, [In] int sessionID, [In, MarshalAs(UnmanagedType.BStr)] string user); + [return: MarshalAs(UnmanagedType.Interface)] + IRunningTaskCollection GetInstances(int flags); + DateTime LastRunTime { get; } + int LastTaskResult { get; } + int NumberOfMissedRuns { get; } + DateTime NextRunTime { get; } + ITaskDefinition Definition { [return: MarshalAs(UnmanagedType.Interface)] get; } + string Xml { [return: MarshalAs(UnmanagedType.BStr)] get; } + [return: MarshalAs(UnmanagedType.BStr)] + string GetSecurityDescriptor(int securityInformation); + void SetSecurityDescriptor([In, MarshalAs(UnmanagedType.BStr)] string sddl, [In] int flags); + void Stop(int flags); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x60020011)] + void GetRunTimes([In] ref NativeMethods.SYSTEMTIME pstStart, [In] ref NativeMethods.SYSTEMTIME pstEnd, [In, Out] ref uint pCount, [In, Out] ref IntPtr pRunTimes); + } + + [ComImport, Guid("86627EB4-42A7-41E4-A4D9-AC33A72F2D52"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IRegisteredTaskCollection + { + int Count { get; } + IRegisteredTask this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + IEnumerator GetEnumerator(); + } + + [ComImport, Guid("416D8B73-CB41-4EA1-805C-9BE9A5AC4A74"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IRegistrationInfo + { + string Description { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Author { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Version { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Date { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Documentation { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string URI { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + object SecurityDescriptor { [return: MarshalAs(UnmanagedType.Struct)] get; [param: In, MarshalAs(UnmanagedType.Struct)] set; } + string Source { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("4C8FEC3A-C218-4E0C-B23D-629024DB91A2"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IRegistrationTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("7FB9ACF1-26BE-400E-85B5-294B9C75DFD6"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IRepetitionPattern + { + string Interval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Duration { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + bool StopAtDurationEnd { get; [param: In] set; } + } + + [ComImport, Guid("653758FB-7B9A-4F1E-A471-BEEB8E9B834E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("InstanceGuid")] + internal interface IRunningTask + { + string Name { [return: MarshalAs(UnmanagedType.BStr)] get; } + string InstanceGuid { [return: MarshalAs(UnmanagedType.BStr)] get; } + string Path { [return: MarshalAs(UnmanagedType.BStr)] get; } + TaskState State { get; } + string CurrentAction { [return: MarshalAs(UnmanagedType.BStr)] get; } + void Stop(); + void Refresh(); + uint EnginePID { get; } + } + + [ComImport, Guid("6A67614B-6828-4FEC-AA54-6D52E8F1F2DB"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IRunningTaskCollection + { + int Count { get; } + IRunningTask this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + IEnumerator GetEnumerator(); + } + + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("754DA71B-4385-4475-9DD9-598294FA3641"), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ISessionStateChangeTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + TaskSessionStateChangeType StateChange { get; [param: In] set; } + } + + [ComImport, Guid("505E9E68-AF89-46B8-A30F-56162A83D537"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IShowMessageAction : IAction + { + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new TaskActionType Type { get; } + + string Title { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string MessageBody { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("F5BC8FC5-536D-4F77-B852-FBC1356FDEB6"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskDefinition + { + IRegistrationInfo RegistrationInfo { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + ITriggerCollection Triggers { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + ITaskSettings Settings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + string Data { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + IPrincipal Principal { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + IActionCollection Actions { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("8CFAC062-A080-4C15-9A88-AA7C2AF80DFC"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Path")] + internal interface ITaskFolder + { + string Name { [return: MarshalAs(UnmanagedType.BStr)] get; } + string Path { [return: MarshalAs(UnmanagedType.BStr)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + ITaskFolder GetFolder([MarshalAs(UnmanagedType.BStr)] string Path); + [return: MarshalAs(UnmanagedType.Interface)] + ITaskFolderCollection GetFolders(int flags); + [return: MarshalAs(UnmanagedType.Interface)] + ITaskFolder CreateFolder([In, MarshalAs(UnmanagedType.BStr)] string subFolderName, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl); + void DeleteFolder([MarshalAs(UnmanagedType.BStr)] string subFolderName, [In] int flags); + [return: MarshalAs(UnmanagedType.Interface)] + IRegisteredTask GetTask([MarshalAs(UnmanagedType.BStr)][NotNull] string Path); + [return: MarshalAs(UnmanagedType.Interface)] + IRegisteredTaskCollection GetTasks(int flags); + void DeleteTask([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Name, [In] int flags); + [return: MarshalAs(UnmanagedType.Interface)] + IRegisteredTask RegisterTask([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path, [In, MarshalAs(UnmanagedType.BStr)][NotNull] string XmlText, [In] int flags, [In, MarshalAs(UnmanagedType.Struct)] object UserId, [In, MarshalAs(UnmanagedType.Struct)] object password, [In] TaskLogonType LogonType, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl); + [return: MarshalAs(UnmanagedType.Interface)] + IRegisteredTask RegisterTaskDefinition([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path, [In, MarshalAs(UnmanagedType.Interface)][NotNull] ITaskDefinition pDefinition, [In] int flags, [In, MarshalAs(UnmanagedType.Struct)] object UserId, [In, MarshalAs(UnmanagedType.Struct)] object password, [In] TaskLogonType LogonType, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl); + [return: MarshalAs(UnmanagedType.BStr)] + string GetSecurityDescriptor(int securityInformation); + void SetSecurityDescriptor([In, MarshalAs(UnmanagedType.BStr)] string sddl, [In] int flags); + } + + [ComImport, Guid("79184A66-8664-423F-97F1-637356A5D812"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskFolderCollection + { + int Count { get; } + ITaskFolder this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + IEnumerator GetEnumerator(); + } + + [ComImport, Guid("B4EF826B-63C3-46E4-A504-EF69E4F7EA4D"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskNamedValueCollection + { + int Count { get; } + ITaskNamedValuePair this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + IEnumerator GetEnumerator(); + [return: MarshalAs(UnmanagedType.Interface)] + ITaskNamedValuePair Create([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.BStr)] string Value); + void Remove([In] int index); + void Clear(); + } + + [ComImport, Guid("39038068-2B46-4AFD-8662-7BB6F868D221"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Name")] + internal interface ITaskNamedValuePair + { + string Name { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string Value { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, DefaultMember("TargetServer"), Guid("2FABA4C7-4DA9-4013-9697-20CC3FD40F85"), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(TaskSchedulerClass))] + internal interface ITaskService + { + [return: MarshalAs(UnmanagedType.Interface)] + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] + ITaskFolder GetFolder([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path); + [return: MarshalAs(UnmanagedType.Interface)] + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] + IRunningTaskCollection GetRunningTasks(int flags); + [return: MarshalAs(UnmanagedType.Interface)] + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] + ITaskDefinition NewTask([In] uint flags); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)] + void Connect([In, Optional, MarshalAs(UnmanagedType.Struct)] object serverName, [In, Optional, MarshalAs(UnmanagedType.Struct)] object user, [In, Optional, MarshalAs(UnmanagedType.Struct)] object domain, [In, Optional, MarshalAs(UnmanagedType.Struct)] object password); + [DispId(5)] + bool Connected { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(5)] get; } + [DispId(0)] + string TargetServer { [return: MarshalAs(UnmanagedType.BStr)][MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0)] get; } + [DispId(6)] + string ConnectedUser { [return: MarshalAs(UnmanagedType.BStr)][MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(6)] get; } + [DispId(7)] + string ConnectedDomain { [return: MarshalAs(UnmanagedType.BStr)][MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(7)] get; } + [DispId(8)] + uint HighestVersion { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(8)] get; } + } + + [ComImport, DefaultMember("TargetServer"), Guid("0F87369F-A4E5-4CFC-BD3E-73E6154572DD"), ClassInterface((short)0), System.Security.SuppressUnmanagedCodeSecurity] + internal class TaskSchedulerClass + { + } + + [ComImport, Guid("8FD4711D-2D02-4C8C-87E3-EFF699DE127E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskSettings + { + bool AllowDemandStart { get; [param: In] set; } + string RestartInterval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + int RestartCount { get; [param: In] set; } + TaskInstancesPolicy MultipleInstances { get; [param: In] set; } + bool StopIfGoingOnBatteries { get; [param: In] set; } + bool DisallowStartIfOnBatteries { get; [param: In] set; } + bool AllowHardTerminate { get; [param: In] set; } + bool StartWhenAvailable { get; [param: In] set; } + string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + bool RunOnlyIfNetworkAvailable { get; [param: In] set; } + string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + bool Enabled { get; [param: In] set; } + string DeleteExpiredTaskAfter { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + int Priority { get; [param: In] set; } + TaskCompatibility Compatibility { get; [param: In] set; } + bool Hidden { get; [param: In] set; } + IIdleSettings IdleSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + bool RunOnlyIfIdle { get; [param: In] set; } + bool WakeToRun { get; [param: In] set; } + INetworkSettings NetworkSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + } + + [ComImport, Guid("2C05C3F0-6EED-4c05-A15F-ED7D7A98A369"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskSettings2 + { + bool DisallowStartOnRemoteAppSession { get; [param: In] set; } + bool UseUnifiedSchedulingEngine { get; [param: In] set; } + } + + [ComImport, Guid("0AD9D0D7-0C7F-4EBB-9A5F-D1C648DCA528"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskSettings3 : ITaskSettings + { + new bool AllowDemandStart { get; [param: In] set; } + new string RestartInterval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new int RestartCount { get; [param: In] set; } + new TaskInstancesPolicy MultipleInstances { get; [param: In] set; } + new bool StopIfGoingOnBatteries { get; [param: In] set; } + new bool DisallowStartIfOnBatteries { get; [param: In] set; } + new bool AllowHardTerminate { get; [param: In] set; } + new bool StartWhenAvailable { get; [param: In] set; } + new string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool RunOnlyIfNetworkAvailable { get; [param: In] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + new string DeleteExpiredTaskAfter { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new int Priority { get; [param: In] set; } + new TaskCompatibility Compatibility { get; [param: In] set; } + new bool Hidden { get; [param: In] set; } + new IIdleSettings IdleSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new bool RunOnlyIfIdle { get; [param: In] set; } + new bool WakeToRun { get; [param: In] set; } + new INetworkSettings NetworkSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + + bool DisallowStartOnRemoteAppSession { get; [param: In] set; } + bool UseUnifiedSchedulingEngine { get; [param: In] set; } + IMaintenanceSettings MaintenanceSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + [return: MarshalAs(UnmanagedType.Interface)] + IMaintenanceSettings CreateMaintenanceSettings(); + bool Volatile { get; [param: In] set; } + } + + [ComImport, Guid("A6024FA8-9652-4ADB-A6BF-5CFCD877A7BA"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IMaintenanceSettings + { + string Period { [param: In, MarshalAs(UnmanagedType.BStr)] set; [return: MarshalAs(UnmanagedType.BStr)] get; } + string Deadline { [param: In, MarshalAs(UnmanagedType.BStr)] set; [return: MarshalAs(UnmanagedType.BStr)] get; } + bool Exclusive { [param: In] set; get; } + } + + [ComImport, Guid("3E4C9351-D966-4B8B-BB87-CEBA68BB0107"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITaskVariables + { + [return: MarshalAs(UnmanagedType.BStr)] + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + string GetInput(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetOutput([In, MarshalAs(UnmanagedType.BStr)] string input); + [return: MarshalAs(UnmanagedType.BStr)] + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + string GetContext(); + } + + [ComImport, Guid("B45747E0-EBA7-4276-9F29-85C5BB300006"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITimeTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } + + [ComImport, Guid("09941815-EA89-4B5B-89E0-2A773801FAC3"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITrigger + { + TaskTriggerType Type { get; } + string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + bool Enabled { get; [param: In] set; } + } + + [ComImport, Guid("85DF5081-1B24-4F32-878A-D9D14DF4CB77"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface ITriggerCollection + { + int Count { get; } + ITrigger this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; } + [return: MarshalAs(UnmanagedType.Interface)] + IEnumerator GetEnumerator(); + [return: MarshalAs(UnmanagedType.Interface)] + ITrigger Create([In] TaskTriggerType Type); + void Remove([In, MarshalAs(UnmanagedType.Struct)] object index); + void Clear(); + } + + [ComImport, Guid("5038FC98-82FF-436D-8728-A512A57C9DC1"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity] + internal interface IWeeklyTrigger : ITrigger + { + new TaskTriggerType Type { get; } + new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; } + new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + new bool Enabled { get; [param: In] set; } + + short DaysOfWeek { get; [param: In] set; } + short WeeksInterval { get; [param: In] set; } + string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Wildcard.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Wildcard.cs index 3c029ad69..fe7e99940 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/Wildcard.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/Wildcard.cs @@ -1,41 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; +using System.Text.RegularExpressions; namespace winPEAS.TaskScheduler { - /// - /// Represents a wildcard running on the - /// engine. - /// - public class Wildcard : Regex - { - /// - /// Initializes a wildcard with the given search pattern and options. - /// - /// The wildcard pattern to match. - /// A combination of one or more . - public Wildcard([NotNull] string pattern, RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace) - : base(WildcardToRegex(pattern), options) - { - } + /// + /// Represents a wildcard running on the + /// engine. + /// + public class Wildcard : Regex + { + /// + /// Initializes a wildcard with the given search pattern and options. + /// + /// The wildcard pattern to match. + /// A combination of one or more . + public Wildcard([NotNull] string pattern, RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace) + : base(WildcardToRegex(pattern), options) + { + } - /// - /// Converts a wildcard to a regular expression. - /// - /// The wildcard pattern to convert. - /// A regular expression equivalent of the given wildcard. - public static string WildcardToRegex([NotNull] string pattern) - { - string s = Regex.Escape(pattern); - s = Regex.Replace(s, @"(? + /// Converts a wildcard to a regular expression. + /// + /// The wildcard pattern to convert. + /// A regular expression equivalent of the given wildcard. + public static string WildcardToRegex([NotNull] string pattern) + { + string s = Escape(pattern); + s = Replace(s, @"(? - /// Impersonation of a user. Allows to execute code under another - /// user context. - /// Please note that the account that instantiates the Impersonator class - /// needs to have the 'Act as part of operating system' privilege set. - /// - internal class WindowsImpersonatedIdentity : IDisposable, IIdentity - { - private const int LOGON_TYPE_NEW_CREDENTIALS = 9; - private const int LOGON32_LOGON_INTERACTIVE = 2; - private const int LOGON32_PROVIDER_DEFAULT = 0; - private const int LOGON32_PROVIDER_WINNT50 = 3; + /// + /// Impersonation of a user. Allows to execute code under another + /// user context. + /// Please note that the account that instantiates the Impersonator class + /// needs to have the 'Act as part of operating system' privilege set. + /// + internal class WindowsImpersonatedIdentity : IDisposable, IIdentity + { + private const int LOGON_TYPE_NEW_CREDENTIALS = 9; + private const int LOGON32_LOGON_INTERACTIVE = 2; + private const int LOGON32_PROVIDER_DEFAULT = 0; + private const int LOGON32_PROVIDER_WINNT50 = 3; #if NETSTANDARD || NETCOREAPP #else - private WindowsImpersonationContext impersonationContext = null; + private WindowsImpersonationContext impersonationContext = null; #endif - SafeTokenHandle token; - private WindowsIdentity identity = null; + SafeTokenHandle token; + private WindowsIdentity identity = null; - /// - /// Constructor. Starts the impersonation with the given credentials. - /// Please note that the account that instantiates the Impersonator class - /// needs to have the 'Act as part of operating system' privilege set. - /// - /// The name of the user to act as. - /// The domain name of the user to act as. - /// The password of the user to act as. - public WindowsImpersonatedIdentity(string userName, string domainName, string password) - { - if (string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(domainName) && string.IsNullOrEmpty(password)) - { - identity = WindowsIdentity.GetCurrent(); - } - else - { - if (Advapi32.LogonUser(userName, domainName, password, domainName == null ? LOGON_TYPE_NEW_CREDENTIALS : LOGON32_LOGON_INTERACTIVE, domainName == null ? LOGON32_PROVIDER_WINNT50 : LOGON32_PROVIDER_DEFAULT, out token) != 0) - { + /// + /// Constructor. Starts the impersonation with the given credentials. + /// Please note that the account that instantiates the Impersonator class + /// needs to have the 'Act as part of operating system' privilege set. + /// + /// The name of the user to act as. + /// The domain name of the user to act as. + /// The password of the user to act as. + public WindowsImpersonatedIdentity(string userName, string domainName, string password) + { + if (string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(domainName) && string.IsNullOrEmpty(password)) + { + identity = WindowsIdentity.GetCurrent(); + } + else + { + if (Advapi32.LogonUser(userName, domainName, password, domainName == null ? LOGON_TYPE_NEW_CREDENTIALS : LOGON32_LOGON_INTERACTIVE, domainName == null ? LOGON32_PROVIDER_WINNT50 : LOGON32_PROVIDER_DEFAULT, out token) != 0) + { #if NETSTANDARD || NETCOREAPP if (!NativeMethods.ImpersonateLoggedOnUser(token.DangerousGetHandle())) throw new Win32Exception(); #else - identity = new WindowsIdentity(token.DangerousGetHandle()); - impersonationContext = identity.Impersonate(); + identity = new WindowsIdentity(token.DangerousGetHandle()); + impersonationContext = identity.Impersonate(); #endif - } - else - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - } - } + } + else + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + } - public string AuthenticationType => identity?.AuthenticationType; + public string AuthenticationType => identity?.AuthenticationType; - public bool IsAuthenticated => identity != null && identity.IsAuthenticated; + public bool IsAuthenticated => identity != null && identity.IsAuthenticated; - public string Name => identity?.Name; + public string Name => identity?.Name; - public void Dispose() - { + public void Dispose() + { #if NETSTANDARD || NETCOREAPP NativeMethods.RevertToSelf(); #else impersonationContext?.Undo(); #endif - token?.Dispose(); + token?.Dispose(); identity?.Dispose(); } - } + } } diff --git a/winPEAS/winPEASexe/winPEAS/TaskScheduler/XmlSerializationHelper.cs b/winPEAS/winPEASexe/winPEAS/TaskScheduler/XmlSerializationHelper.cs index 5e36e9f3c..b182b18f6 100644 --- a/winPEAS/winPEASexe/winPEAS/TaskScheduler/XmlSerializationHelper.cs +++ b/winPEAS/winPEASexe/winPEAS/TaskScheduler/XmlSerializationHelper.cs @@ -1,422 +1,420 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using System.Reflection; using System.Text; -using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; namespace winPEAS.TaskScheduler { - internal static class XmlSerializationHelper - { - public static object GetDefaultValue([NotNull] PropertyInfo prop) - { - var attributes = prop.GetCustomAttributes(typeof(DefaultValueAttribute), true); - if (attributes.Length > 0) - { - var defaultAttr = (DefaultValueAttribute)attributes[0]; - return defaultAttr.Value; - } - - // Attribute not found, fall back to default value for the type - if (prop.PropertyType.IsValueType) - return Activator.CreateInstance(prop.PropertyType); - return null; - } - - private static bool GetPropertyValue(object obj, [NotNull] string property, ref object outVal) - { - PropertyInfo pi = obj?.GetType().GetProperty(property); - if (pi != null) - { - outVal = pi.GetValue(obj, null); - return true; - } - return false; - } - - private static bool GetAttributeValue(Type objType, Type attrType, string property, bool inherit, ref object outVal) - { - object[] attrs = objType.GetCustomAttributes(attrType, inherit); - if (attrs.Length > 0) - return GetPropertyValue(attrs[0], property, ref outVal); - return false; - } - - private static bool GetAttributeValue([NotNull] PropertyInfo propInfo, Type attrType, string property, bool inherit, ref object outVal) - { - Attribute attr = Attribute.GetCustomAttribute(propInfo, attrType, inherit); - return GetPropertyValue(attr, property, ref outVal); - } - - private static bool IsStandardType(Type type) => type.IsPrimitive || type == typeof(DateTime) || type == typeof(DateTimeOffset) || type == typeof(Decimal) || type == typeof(Guid) || type == typeof(TimeSpan) || type == typeof(string) || type.IsEnum; - - private static bool HasMembers([NotNull] object obj) - { - if (obj is IXmlSerializable) - { - using (System.IO.MemoryStream mem = new System.IO.MemoryStream()) - { - using (XmlTextWriter tw = new XmlTextWriter(mem, Encoding.UTF8)) - { - ((IXmlSerializable)obj).WriteXml(tw); - tw.Flush(); - return mem.Length > 3; - } - } - } - - // Enumerate each public property - PropertyInfo[] props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); - foreach (var pi in props) - { - if (!Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false)) - { - object value = pi.GetValue(obj, null); - if (!Equals(value, GetDefaultValue(pi))) - { - if (!IsStandardType(pi.PropertyType)) - { - if (HasMembers(value)) - return true; - } - else - return true; - } - } - } - return false; - } - - public static string GetPropertyAttributeName([NotNull] PropertyInfo pi) - { - object oVal = null; - string eName = pi.Name; - if (GetAttributeValue(pi, typeof(XmlAttributeAttribute), "AttributeName", false, ref oVal)) - eName = oVal.ToString(); - return eName; - } - - public static string GetPropertyElementName([NotNull] PropertyInfo pi) - { - object oVal = null; - string eName = pi.Name; - if (GetAttributeValue(pi, typeof(XmlElementAttribute), "ElementName", false, ref oVal)) - eName = oVal.ToString(); - else if (GetAttributeValue(pi.PropertyType, typeof(XmlRootAttribute), "ElementName", true, ref oVal)) - eName = oVal.ToString(); - return eName; - } - - public delegate bool PropertyConversionHandler([NotNull] PropertyInfo pi, Object obj, ref Object value); - - public static bool WriteProperty([NotNull] XmlWriter writer, [NotNull] PropertyInfo pi, [NotNull] Object obj, PropertyConversionHandler handler = null) - { - if (Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false) || Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false)) - return false; - - object value = pi.GetValue(obj, null); - object defValue = GetDefaultValue(pi); - if ((value == null && defValue == null) || (value != null && value.Equals(defValue))) - return false; - - Type propType = pi.PropertyType; - if (handler != null && handler(pi, obj, ref value)) - propType = value.GetType(); - - bool isStdType = IsStandardType(propType); - bool rw = pi.CanRead && pi.CanWrite; - bool ro = pi.CanRead && !pi.CanWrite; - string eName = GetPropertyElementName(pi); - if (isStdType && rw) - { - string output = GetXmlValue(value, propType); - if (output != null) - writer.WriteElementString(eName, output); - } - else if (!isStdType) - { - object outVal = null; - if (propType.GetInterface("IXmlSerializable") == null && GetAttributeValue(pi, typeof(XmlArrayAttribute), "ElementName", true, ref outVal) && propType.GetInterface("IEnumerable") != null) - { - if (string.IsNullOrEmpty(outVal.ToString())) outVal = eName; - writer.WriteStartElement(outVal.ToString()); - var attributes = Attribute.GetCustomAttributes(pi, typeof(XmlArrayItemAttribute), true); - var dict = new Dictionary(attributes.Length); - foreach (XmlArrayItemAttribute a in attributes) - dict.Add(a.Type, a.ElementName); - foreach (object item in ((System.Collections.IEnumerable)value)) - { - string aeName; - Type itemType = item.GetType(); - if (dict.TryGetValue(itemType, out aeName)) - { - if (IsStandardType(itemType)) - writer.WriteElementString(aeName, GetXmlValue(item, itemType)); - else - WriteObject(writer, item, null, false, aeName); - } - } - writer.WriteEndElement(); - } - else - WriteObject(writer, value); - } - return false; - } - - private static string GetXmlValue([NotNull] object value, Type propType) - { - string output = null; - if (propType.IsEnum) - { - if (Attribute.IsDefined(propType, typeof(FlagsAttribute), false)) - output = Convert.ChangeType(value, Enum.GetUnderlyingType(propType)).ToString(); - else - output = value.ToString(); - } - else - { - switch (propType.FullName) - { - case "System.Boolean": - output = XmlConvert.ToString((System.Boolean)value); - break; - case "System.Byte": - output = XmlConvert.ToString((System.Byte)value); - break; - case "System.Char": - output = XmlConvert.ToString((System.Char)value); - break; - case "System.DateTime": - output = XmlConvert.ToString((System.DateTime)value, XmlDateTimeSerializationMode.RoundtripKind); - break; - case "System.DateTimeOffset": - output = XmlConvert.ToString((System.DateTimeOffset)value); - break; - case "System.Decimal": - output = XmlConvert.ToString((System.Decimal)value); - break; - case "System.Double": - output = XmlConvert.ToString((System.Double)value); - break; - case "System.Single": - output = XmlConvert.ToString((System.Single)value); - break; - case "System.Guid": - output = XmlConvert.ToString((System.Guid)value); - break; - case "System.Int16": - output = XmlConvert.ToString((System.Int16)value); - break; - case "System.Int32": - output = XmlConvert.ToString((System.Int32)value); - break; - case "System.Int64": - output = XmlConvert.ToString((System.Int64)value); - break; - case "System.SByte": - output = XmlConvert.ToString((System.SByte)value); - break; - case "System.TimeSpan": - output = XmlConvert.ToString((System.TimeSpan)value); - break; - case "System.UInt16": - output = XmlConvert.ToString((System.UInt16)value); - break; - case "System.UInt32": - output = XmlConvert.ToString((System.UInt32)value); - break; - case "System.UInt64": - output = XmlConvert.ToString((System.UInt64)value); - break; - default: - output = value == null ? string.Empty : value.ToString(); - break; - } - } - - return output; - } - - public static void WriteObjectAttributes([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null) - { - // Enumerate each property - foreach (var pi in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) - if (Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false)) - WriteObjectAttribute(writer, pi, obj, handler); - } - - public static void WriteObjectAttribute([NotNull] XmlWriter writer, [NotNull] PropertyInfo pi, [NotNull] object obj, PropertyConversionHandler handler = null) - { - object value = pi.GetValue(obj, null); - object defValue = GetDefaultValue(pi); - if ((value == null && defValue == null) || (value != null && value.Equals(defValue))) - return; - - Type propType = pi.PropertyType; - if (handler != null && handler(pi, obj, ref value)) - propType = value.GetType(); - - writer.WriteAttributeString(GetPropertyAttributeName(pi), GetXmlValue(value, propType)); - } - - public static void WriteObjectProperties([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null) - { - // Enumerate each public property - foreach (var pi in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) - WriteProperty(writer, pi, obj, handler); - } - - public static void WriteObject([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null, bool includeNS = false, string elemName = null) - { - if (obj == null) - return; - - // Get name of top level element - string oName = elemName ?? GetElementName(obj); - - if (!HasMembers(obj)) - return; - - if (includeNS) - writer.WriteStartElement(oName, GetTopLevelNamespace(obj)); - else - writer.WriteStartElement(oName); - - if (obj is IXmlSerializable) - { - ((IXmlSerializable)obj).WriteXml(writer); - } - else - { - WriteObjectAttributes(writer, obj, handler); - WriteObjectProperties(writer, obj, handler); - } - - writer.WriteEndElement(); - } - - public static string GetElementName([NotNull] object obj) - { - object oVal = null; - return GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "ElementName", true, ref oVal) ? oVal.ToString() : obj.GetType().Name; - } - - public static string GetTopLevelNamespace([NotNull] object obj) - { - object oVal = null; - return GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "Namespace", true, ref oVal) ? oVal.ToString() : null; - } - - public static void ReadObjectProperties([NotNull] XmlReader reader, [NotNull] object obj, PropertyConversionHandler handler = null) - { - // Build property lookup table - PropertyInfo[] props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); - Dictionary attrHash = new Dictionary(props.Length); - Dictionary propHash = new Dictionary(props.Length); - foreach (var pi in props) - { - if (!Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false)) - { - if (Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false)) - attrHash.Add(GetPropertyAttributeName(pi), pi); - else - propHash.Add(GetPropertyElementName(pi), pi); - } - } - - if (reader.HasAttributes) - { - for (int i = 0; i < reader.AttributeCount; i++) - { - PropertyInfo pi; - reader.MoveToAttribute(i); - if (attrHash.TryGetValue(reader.LocalName, out pi)) - { - if (IsStandardType(pi.PropertyType)) - { - object value = null; - if (pi.PropertyType.IsEnum) - value = Enum.Parse(pi.PropertyType, reader.Value); - else - value = Convert.ChangeType(reader.Value, pi.PropertyType); - - if (handler != null) - handler(pi, obj, ref value); - - pi.SetValue(obj, value, null); - } - } - } - } - - while (reader.MoveToContent() == XmlNodeType.Element) - { - PropertyInfo pi; - object outVal = null; - if (propHash.TryGetValue(reader.LocalName, out pi)) - { - var tc = TypeDescriptor.GetConverter(pi.PropertyType); - if (IsStandardType(pi.PropertyType)) - { - object value = null; - if (pi.PropertyType.IsEnum) - value = Enum.Parse(pi.PropertyType, reader.ReadElementContentAsString()); - else if (pi.PropertyType == typeof(Guid)) - value = new GuidConverter().ConvertFromString(reader.ReadElementContentAsString()); - else - value = reader.ReadElementContentAs(pi.PropertyType, null); - - if (handler != null) - handler(pi, obj, ref value); - - pi.SetValue(obj, value, null); - } - else if (pi.PropertyType == typeof(Version)) - { - Version v = new Version(reader.ReadElementContentAsString()); - pi.SetValue(obj, v, null); - } - else if (pi.PropertyType.GetInterface("IEnumerable") != null && pi.PropertyType.GetInterface("IXmlSerializable") == null && GetAttributeValue(pi, typeof(XmlArrayAttribute), "ElementName", true, ref outVal)) - { - string elem = string.IsNullOrEmpty(outVal?.ToString()) ? pi.Name : outVal.ToString(); - reader.ReadStartElement(elem); - var attributes = Attribute.GetCustomAttributes(pi, typeof(XmlArrayItemAttribute), true); - var dict = new Dictionary(attributes.Length); - foreach (XmlArrayItemAttribute a in attributes) - dict.Add(a.ElementName, a.Type); - List output = new List(); - while (reader.MoveToContent() == XmlNodeType.Element) - { - Type itemType; - if (dict.TryGetValue(reader.LocalName, out itemType)) - { - object o; - if (IsStandardType(itemType)) - o = reader.ReadElementContentAs(itemType, null); - else - { - o = Activator.CreateInstance(itemType); - ReadObject(reader, o, handler); - } - if (o != null) - output.Add(o); - } - } - reader.ReadEndElement(); - if (output.Count > 0) - { - System.Collections.IEnumerable par = output; - Type et = typeof(object); - if (dict.Count == 1) - { - foreach (var v in dict.Values) { et = v; break; } - } - /*else + internal static class XmlSerializationHelper + { + public static object GetDefaultValue([NotNull] PropertyInfo prop) + { + var attributes = prop.GetCustomAttributes(typeof(DefaultValueAttribute), true); + if (attributes.Length > 0) + { + var defaultAttr = (DefaultValueAttribute)attributes[0]; + return defaultAttr.Value; + } + + // Attribute not found, fall back to default value for the type + if (prop.PropertyType.IsValueType) + return Activator.CreateInstance(prop.PropertyType); + return null; + } + + private static bool GetPropertyValue(object obj, [NotNull] string property, ref object outVal) + { + PropertyInfo pi = obj?.GetType().GetProperty(property); + if (pi != null) + { + outVal = pi.GetValue(obj, null); + return true; + } + return false; + } + + private static bool GetAttributeValue(Type objType, Type attrType, string property, bool inherit, ref object outVal) + { + object[] attrs = objType.GetCustomAttributes(attrType, inherit); + if (attrs.Length > 0) + return GetPropertyValue(attrs[0], property, ref outVal); + return false; + } + + private static bool GetAttributeValue([NotNull] PropertyInfo propInfo, Type attrType, string property, bool inherit, ref object outVal) + { + Attribute attr = Attribute.GetCustomAttribute(propInfo, attrType, inherit); + return GetPropertyValue(attr, property, ref outVal); + } + + private static bool IsStandardType(Type type) => type.IsPrimitive || type == typeof(DateTime) || type == typeof(DateTimeOffset) || type == typeof(Decimal) || type == typeof(Guid) || type == typeof(TimeSpan) || type == typeof(string) || type.IsEnum; + + private static bool HasMembers([NotNull] object obj) + { + if (obj is IXmlSerializable) + { + using (System.IO.MemoryStream mem = new System.IO.MemoryStream()) + { + using (XmlTextWriter tw = new XmlTextWriter(mem, Encoding.UTF8)) + { + ((IXmlSerializable)obj).WriteXml(tw); + tw.Flush(); + return mem.Length > 3; + } + } + } + + // Enumerate each public property + PropertyInfo[] props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); + foreach (var pi in props) + { + if (!Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false)) + { + object value = pi.GetValue(obj, null); + if (!Equals(value, GetDefaultValue(pi))) + { + if (!IsStandardType(pi.PropertyType)) + { + if (HasMembers(value)) + return true; + } + else + return true; + } + } + } + return false; + } + + public static string GetPropertyAttributeName([NotNull] PropertyInfo pi) + { + object oVal = null; + string eName = pi.Name; + if (GetAttributeValue(pi, typeof(XmlAttributeAttribute), "AttributeName", false, ref oVal)) + eName = oVal.ToString(); + return eName; + } + + public static string GetPropertyElementName([NotNull] PropertyInfo pi) + { + object oVal = null; + string eName = pi.Name; + if (GetAttributeValue(pi, typeof(XmlElementAttribute), "ElementName", false, ref oVal)) + eName = oVal.ToString(); + else if (GetAttributeValue(pi.PropertyType, typeof(XmlRootAttribute), "ElementName", true, ref oVal)) + eName = oVal.ToString(); + return eName; + } + + public delegate bool PropertyConversionHandler([NotNull] PropertyInfo pi, Object obj, ref Object value); + + public static bool WriteProperty([NotNull] XmlWriter writer, [NotNull] PropertyInfo pi, [NotNull] Object obj, PropertyConversionHandler handler = null) + { + if (Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false) || Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false)) + return false; + + object value = pi.GetValue(obj, null); + object defValue = GetDefaultValue(pi); + if ((value == null && defValue == null) || (value != null && value.Equals(defValue))) + return false; + + Type propType = pi.PropertyType; + if (handler != null && handler(pi, obj, ref value)) + propType = value.GetType(); + + bool isStdType = IsStandardType(propType); + bool rw = pi.CanRead && pi.CanWrite; + bool ro = pi.CanRead && !pi.CanWrite; + string eName = GetPropertyElementName(pi); + if (isStdType && rw) + { + string output = GetXmlValue(value, propType); + if (output != null) + writer.WriteElementString(eName, output); + } + else if (!isStdType) + { + object outVal = null; + if (propType.GetInterface("IXmlSerializable") == null && GetAttributeValue(pi, typeof(XmlArrayAttribute), "ElementName", true, ref outVal) && propType.GetInterface("IEnumerable") != null) + { + if (string.IsNullOrEmpty(outVal.ToString())) outVal = eName; + writer.WriteStartElement(outVal.ToString()); + var attributes = Attribute.GetCustomAttributes(pi, typeof(XmlArrayItemAttribute), true); + var dict = new Dictionary(attributes.Length); + foreach (XmlArrayItemAttribute a in attributes) + dict.Add(a.Type, a.ElementName); + foreach (object item in ((System.Collections.IEnumerable)value)) + { + string aeName; + Type itemType = item.GetType(); + if (dict.TryGetValue(itemType, out aeName)) + { + if (IsStandardType(itemType)) + writer.WriteElementString(aeName, GetXmlValue(item, itemType)); + else + WriteObject(writer, item, null, false, aeName); + } + } + writer.WriteEndElement(); + } + else + WriteObject(writer, value); + } + return false; + } + + private static string GetXmlValue([NotNull] object value, Type propType) + { + string output = null; + if (propType.IsEnum) + { + if (Attribute.IsDefined(propType, typeof(FlagsAttribute), false)) + output = Convert.ChangeType(value, Enum.GetUnderlyingType(propType)).ToString(); + else + output = value.ToString(); + } + else + { + switch (propType.FullName) + { + case "System.Boolean": + output = XmlConvert.ToString((System.Boolean)value); + break; + case "System.Byte": + output = XmlConvert.ToString((System.Byte)value); + break; + case "System.Char": + output = XmlConvert.ToString((System.Char)value); + break; + case "System.DateTime": + output = XmlConvert.ToString((System.DateTime)value, XmlDateTimeSerializationMode.RoundtripKind); + break; + case "System.DateTimeOffset": + output = XmlConvert.ToString((System.DateTimeOffset)value); + break; + case "System.Decimal": + output = XmlConvert.ToString((System.Decimal)value); + break; + case "System.Double": + output = XmlConvert.ToString((System.Double)value); + break; + case "System.Single": + output = XmlConvert.ToString((System.Single)value); + break; + case "System.Guid": + output = XmlConvert.ToString((System.Guid)value); + break; + case "System.Int16": + output = XmlConvert.ToString((System.Int16)value); + break; + case "System.Int32": + output = XmlConvert.ToString((System.Int32)value); + break; + case "System.Int64": + output = XmlConvert.ToString((System.Int64)value); + break; + case "System.SByte": + output = XmlConvert.ToString((System.SByte)value); + break; + case "System.TimeSpan": + output = XmlConvert.ToString((System.TimeSpan)value); + break; + case "System.UInt16": + output = XmlConvert.ToString((System.UInt16)value); + break; + case "System.UInt32": + output = XmlConvert.ToString((System.UInt32)value); + break; + case "System.UInt64": + output = XmlConvert.ToString((System.UInt64)value); + break; + default: + output = value == null ? string.Empty : value.ToString(); + break; + } + } + + return output; + } + + public static void WriteObjectAttributes([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null) + { + // Enumerate each property + foreach (var pi in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) + if (Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false)) + WriteObjectAttribute(writer, pi, obj, handler); + } + + public static void WriteObjectAttribute([NotNull] XmlWriter writer, [NotNull] PropertyInfo pi, [NotNull] object obj, PropertyConversionHandler handler = null) + { + object value = pi.GetValue(obj, null); + object defValue = GetDefaultValue(pi); + if ((value == null && defValue == null) || (value != null && value.Equals(defValue))) + return; + + Type propType = pi.PropertyType; + if (handler != null && handler(pi, obj, ref value)) + propType = value.GetType(); + + writer.WriteAttributeString(GetPropertyAttributeName(pi), GetXmlValue(value, propType)); + } + + public static void WriteObjectProperties([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null) + { + // Enumerate each public property + foreach (var pi in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) + WriteProperty(writer, pi, obj, handler); + } + + public static void WriteObject([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null, bool includeNS = false, string elemName = null) + { + if (obj == null) + return; + + // Get name of top level element + string oName = elemName ?? GetElementName(obj); + + if (!HasMembers(obj)) + return; + + if (includeNS) + writer.WriteStartElement(oName, GetTopLevelNamespace(obj)); + else + writer.WriteStartElement(oName); + + if (obj is IXmlSerializable) + { + ((IXmlSerializable)obj).WriteXml(writer); + } + else + { + WriteObjectAttributes(writer, obj, handler); + WriteObjectProperties(writer, obj, handler); + } + + writer.WriteEndElement(); + } + + public static string GetElementName([NotNull] object obj) + { + object oVal = null; + return GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "ElementName", true, ref oVal) ? oVal.ToString() : obj.GetType().Name; + } + + public static string GetTopLevelNamespace([NotNull] object obj) + { + object oVal = null; + return GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "Namespace", true, ref oVal) ? oVal.ToString() : null; + } + + public static void ReadObjectProperties([NotNull] XmlReader reader, [NotNull] object obj, PropertyConversionHandler handler = null) + { + // Build property lookup table + PropertyInfo[] props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); + Dictionary attrHash = new Dictionary(props.Length); + Dictionary propHash = new Dictionary(props.Length); + foreach (var pi in props) + { + if (!Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false)) + { + if (Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false)) + attrHash.Add(GetPropertyAttributeName(pi), pi); + else + propHash.Add(GetPropertyElementName(pi), pi); + } + } + + if (reader.HasAttributes) + { + for (int i = 0; i < reader.AttributeCount; i++) + { + PropertyInfo pi; + reader.MoveToAttribute(i); + if (attrHash.TryGetValue(reader.LocalName, out pi)) + { + if (IsStandardType(pi.PropertyType)) + { + object value = null; + if (pi.PropertyType.IsEnum) + value = Enum.Parse(pi.PropertyType, reader.Value); + else + value = Convert.ChangeType(reader.Value, pi.PropertyType); + + if (handler != null) + handler(pi, obj, ref value); + + pi.SetValue(obj, value, null); + } + } + } + } + + while (reader.MoveToContent() == XmlNodeType.Element) + { + PropertyInfo pi; + object outVal = null; + if (propHash.TryGetValue(reader.LocalName, out pi)) + { + var tc = TypeDescriptor.GetConverter(pi.PropertyType); + if (IsStandardType(pi.PropertyType)) + { + object value = null; + if (pi.PropertyType.IsEnum) + value = Enum.Parse(pi.PropertyType, reader.ReadElementContentAsString()); + else if (pi.PropertyType == typeof(Guid)) + value = new GuidConverter().ConvertFromString(reader.ReadElementContentAsString()); + else + value = reader.ReadElementContentAs(pi.PropertyType, null); + + if (handler != null) + handler(pi, obj, ref value); + + pi.SetValue(obj, value, null); + } + else if (pi.PropertyType == typeof(Version)) + { + Version v = new Version(reader.ReadElementContentAsString()); + pi.SetValue(obj, v, null); + } + else if (pi.PropertyType.GetInterface("IEnumerable") != null && pi.PropertyType.GetInterface("IXmlSerializable") == null && GetAttributeValue(pi, typeof(XmlArrayAttribute), "ElementName", true, ref outVal)) + { + string elem = string.IsNullOrEmpty(outVal?.ToString()) ? pi.Name : outVal.ToString(); + reader.ReadStartElement(elem); + var attributes = Attribute.GetCustomAttributes(pi, typeof(XmlArrayItemAttribute), true); + var dict = new Dictionary(attributes.Length); + foreach (XmlArrayItemAttribute a in attributes) + dict.Add(a.ElementName, a.Type); + List output = new List(); + while (reader.MoveToContent() == XmlNodeType.Element) + { + Type itemType; + if (dict.TryGetValue(reader.LocalName, out itemType)) + { + object o; + if (IsStandardType(itemType)) + o = reader.ReadElementContentAs(itemType, null); + else + { + o = Activator.CreateInstance(itemType); + ReadObject(reader, o, handler); + } + if (o != null) + output.Add(o); + } + } + reader.ReadEndElement(); + if (output.Count > 0) + { + System.Collections.IEnumerable par = output; + Type et = typeof(object); + if (dict.Count == 1) + { + foreach (var v in dict.Values) { et = v; break; } + } + /*else { Type t1 = output[0].GetType(); bool same = true; @@ -434,94 +432,94 @@ public static void ReadObjectProperties([NotNull] XmlReader reader, [NotNull] ob } else par = output.ToArray();*/ - bool done = false; - if (pi.PropertyType == par.GetType() || (pi.PropertyType.IsArray && (pi.PropertyType.GetElementType() == typeof(object) || pi.PropertyType.GetElementType() == et))) - try { pi.SetValue(obj, par, null); done = true; } catch { } - if (!done) - { - var mi = pi.PropertyType.GetMethod("AddRange", new Type[] { typeof(System.Collections.IEnumerable) }); - if (mi != null) - try { mi.Invoke(pi.GetValue(obj, null), new object[] { par }); done = true; } catch { } - } - if (!done) - { - var mi = pi.PropertyType.GetMethod("Add", new Type[] { typeof(object) }); - if (mi != null) - try { foreach (var i in par) mi.Invoke(pi.GetValue(obj, null), new object[] { i }); done = true; } catch { } - } - if (!done && et != typeof(Object)) - { - var mi = pi.PropertyType.GetMethod("Add", new Type[] { et }); - if (mi != null) - try { foreach (var i in par) mi.Invoke(pi.GetValue(obj, null), new object[] { i }); done = true; } catch { } - } - // Throw error if not done - } - } - else - { - object inst = pi.GetValue(obj, null) ?? Activator.CreateInstance(pi.PropertyType); - if (inst == null) - throw new InvalidOperationException($"Can't get instance of {pi.PropertyType.Name}."); - ReadObject(reader, inst, handler); - } - } - else - { - reader.Skip(); - reader.MoveToContent(); - } - } - } - - public static void ReadObject([NotNull] XmlReader reader, [NotNull] object obj, PropertyConversionHandler handler = null) - { - if (obj == null) - throw new ArgumentNullException(nameof(obj)); - - reader.MoveToContent(); - - if (obj is IXmlSerializable) - { - ((IXmlSerializable)obj).ReadXml(reader); - } - else - { - object oVal = null; - string oName = GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "ElementName", true, ref oVal) ? oVal.ToString() : obj.GetType().Name; - if (reader.LocalName != oName) - throw new XmlException("XML element name does not match object."); - - if (!reader.IsEmptyElement) - { - reader.ReadStartElement(); - reader.MoveToContent(); - ReadObjectProperties(reader, obj, handler); - reader.ReadEndElement(); - } - else - reader.Skip(); - } - } - - public static void ReadObjectFromXmlText([NotNull] string xml, [NotNull] object obj, PropertyConversionHandler handler = null) - { - using (System.IO.StringReader sr = new System.IO.StringReader(xml)) - { - using (XmlReader reader = XmlReader.Create(sr)) - { - reader.MoveToContent(); - ReadObject(reader, obj, handler); - } - } - } - - public static string WriteObjectToXmlText([NotNull] object obj, PropertyConversionHandler handler = null) - { - StringBuilder sb = new StringBuilder(); - using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true })) - WriteObject(writer, obj, handler, true); - return sb.ToString(); - } - } + bool done = false; + if (pi.PropertyType == par.GetType() || (pi.PropertyType.IsArray && (pi.PropertyType.GetElementType() == typeof(object) || pi.PropertyType.GetElementType() == et))) + try { pi.SetValue(obj, par, null); done = true; } catch { } + if (!done) + { + var mi = pi.PropertyType.GetMethod("AddRange", new Type[] { typeof(System.Collections.IEnumerable) }); + if (mi != null) + try { mi.Invoke(pi.GetValue(obj, null), new object[] { par }); done = true; } catch { } + } + if (!done) + { + var mi = pi.PropertyType.GetMethod("Add", new Type[] { typeof(object) }); + if (mi != null) + try { foreach (var i in par) mi.Invoke(pi.GetValue(obj, null), new object[] { i }); done = true; } catch { } + } + if (!done && et != typeof(Object)) + { + var mi = pi.PropertyType.GetMethod("Add", new Type[] { et }); + if (mi != null) + try { foreach (var i in par) mi.Invoke(pi.GetValue(obj, null), new object[] { i }); done = true; } catch { } + } + // Throw error if not done + } + } + else + { + object inst = pi.GetValue(obj, null) ?? Activator.CreateInstance(pi.PropertyType); + if (inst == null) + throw new InvalidOperationException($"Can't get instance of {pi.PropertyType.Name}."); + ReadObject(reader, inst, handler); + } + } + else + { + reader.Skip(); + reader.MoveToContent(); + } + } + } + + public static void ReadObject([NotNull] XmlReader reader, [NotNull] object obj, PropertyConversionHandler handler = null) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + reader.MoveToContent(); + + if (obj is IXmlSerializable) + { + ((IXmlSerializable)obj).ReadXml(reader); + } + else + { + object oVal = null; + string oName = GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "ElementName", true, ref oVal) ? oVal.ToString() : obj.GetType().Name; + if (reader.LocalName != oName) + throw new XmlException("XML element name does not match object."); + + if (!reader.IsEmptyElement) + { + reader.ReadStartElement(); + reader.MoveToContent(); + ReadObjectProperties(reader, obj, handler); + reader.ReadEndElement(); + } + else + reader.Skip(); + } + } + + public static void ReadObjectFromXmlText([NotNull] string xml, [NotNull] object obj, PropertyConversionHandler handler = null) + { + using (System.IO.StringReader sr = new System.IO.StringReader(xml)) + { + using (XmlReader reader = XmlReader.Create(sr)) + { + reader.MoveToContent(); + ReadObject(reader, obj, handler); + } + } + } + + public static string WriteObjectToXmlText([NotNull] object obj, PropertyConversionHandler handler = null) + { + StringBuilder sb = new StringBuilder(); + using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true })) + WriteObject(writer, obj, handler, true); + return sb.ToString(); + } + } } diff --git a/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/Interop.cs b/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/Interop.cs index d4a9a92b8..e2af130d8 100644 --- a/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/Interop.cs +++ b/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/Interop.cs @@ -1,20 +1,18 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Diagnostics; -using System.Runtime.InteropServices; namespace winPEAS.Wifi.NativeWifiApi { public static class Wlan { #region P/Invoke API - + public const uint WLAN_CLIENT_VERSION_XP_SP2 = 1; public const uint WLAN_CLIENT_VERSION_LONGHORN = 2; public const uint WLAN_MAX_NAME_LENGTH = 256; - - + + #endregion /// diff --git a/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/WlanClient.cs b/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/WlanClient.cs index 72f6540e9..a306639a2 100644 --- a/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/WlanClient.cs +++ b/winPEAS/winPEASexe/winPEAS/Wifi/NativeWifiApi/WlanClient.cs @@ -13,12 +13,12 @@ internal class WlanClient public class WlanInterface { private WlanClient client; - private WlanInterfaceInfo info; + private WlanInterfaceInfo info; internal WlanInterface(WlanClient client, WlanInterfaceInfo info) { this.client = client; - this.info = info; + this.info = info; } /// @@ -33,7 +33,7 @@ public string GetProfileXml(string profileName, bool unencryptedPassword = true) Wlan.ThrowIfError( WlanApi.WlanGetProfile( client.clientHandle, info.interfaceGuid, profileName, IntPtr.Zero, out var profileXmlPtr, out flags, out _)); - + try { return Marshal.PtrToStringUni(profileXmlPtr); @@ -54,7 +54,7 @@ public WlanProfileInfo[] GetProfiles() WlanApi.WlanGetProfileList(client.clientHandle, info.interfaceGuid, IntPtr.Zero, out var profileListPtr)); try { - var header = + var header = (WlanProfileInfoListHeader)Marshal.PtrToStructure(profileListPtr, typeof(WlanProfileInfoListHeader)); WlanProfileInfo[] profileInfos = new WlanProfileInfo[header.numberOfItems]; long profileListIterator = profileListPtr.ToInt64() + Marshal.SizeOf(header); @@ -73,7 +73,7 @@ public WlanProfileInfo[] GetProfiles() { WlanApi.WlanFreeMemory(profileListPtr); } - } + } } private IntPtr clientHandle; @@ -85,7 +85,7 @@ public WlanClient() { Wlan.ThrowIfError( WlanApi.WlanOpenHandle( - Wlan.WLAN_CLIENT_VERSION_XP_SP2, IntPtr.Zero, out negotiatedVersion, out clientHandle)); + Wlan.WLAN_CLIENT_VERSION_XP_SP2, IntPtr.Zero, out negotiatedVersion, out clientHandle)); } ~WlanClient() @@ -93,9 +93,9 @@ public WlanClient() if (clientHandle != IntPtr.Zero) { WlanApi.WlanCloseHandle(clientHandle, IntPtr.Zero); - } + } } - + /// /// Gets the WLAN interfaces. /// @@ -117,12 +117,12 @@ public WlanInterface[] Interfaces Int64 listIterator = ifaceList.ToInt64() + Marshal.SizeOf(header); WlanInterface[] interfaces = new WlanInterface[header.numberOfItems]; List currentIfaceGuids = new List(); - + for (int i = 0; i < header.numberOfItems; ++i) { var info = (WlanInterfaceInfo)Marshal.PtrToStructure( - new IntPtr(listIterator), + new IntPtr(listIterator), typeof(WlanInterfaceInfo)); listIterator += Marshal.SizeOf(info); @@ -138,13 +138,13 @@ public WlanInterface[] Interfaces // Remove stale interfaces var deadIfacesGuids = new Queue(); - + foreach (Guid ifaceGuid in ifaces.Keys) { if (!currentIfaceGuids.Contains(ifaceGuid)) deadIfacesGuids.Enqueue(ifaceGuid); } - + while (deadIfacesGuids.Count != 0) { Guid deadIfaceGuid = deadIfacesGuids.Dequeue(); diff --git a/winPEAS/winPEASexe/winPEAS/Wifi/Wifi.cs b/winPEAS/winPEASexe/winPEAS/Wifi/Wifi.cs index 77b1d0d24..deff06653 100644 --- a/winPEAS/winPEASexe/winPEAS/Wifi/Wifi.cs +++ b/winPEAS/winPEASexe/winPEAS/Wifi/Wifi.cs @@ -39,7 +39,7 @@ private static IEnumerable GetSSIDs() private static string GetPassword(string ssid) { - string args = $@" wlan show profile name=""{ssid}"" key=""clear"""; + string args = $@" wlan show profile name=""{ssid}"" key=""clear"""; string result = MyUtils.ExecCMD(args, "netsh"); Regex regex = new Regex(@"Key Content\s+:\s+([^\r\n]+)", RegexOptions.Multiline); MatchCollection matches = regex.Matches(result);