1
mirror of https://github.com/carlospolop/PEASS-ng synced 2024-11-24 01:26:22 +01:00

- scheduled tasks rewrite - cleanup, excluding also Microsoft from Path; skipping failed scheduled tasks

- added total execution time for debugging purposes
- bugfixes
- GetEverLoggedUsers() - skipping users which could not be translated
This commit is contained in:
makikvues 2021-01-18 22:12:01 +01:00
parent e16234b773
commit 9dd1fdbc95
48 changed files with 7974 additions and 2679 deletions

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Security.Principal;
@ -36,6 +35,9 @@ namespace winPEAS.Checks
//static string paint_lockoutUsers = "";
public static string PaintAdminUsers = "";
private static List<SystemCheck> _systemChecks;
private static HashSet<string> _systemCheckSelectedKeysHashSet = new HashSet<string>();
class SystemCheck
{
public string Key { get; }
@ -54,7 +56,7 @@ namespace winPEAS.Checks
bool isAllChecks = true;
bool wait = false;
var systemChecks = new List<SystemCheck>
_systemChecks = new List<SystemCheck>
{
new SystemCheck("systeminfo", new SystemInfo()),
new SystemCheck("userinfo", new UserInfo()),
@ -67,8 +69,7 @@ namespace winPEAS.Checks
new SystemCheck("filesinfo", new FilesInfo()),
};
var systemCheckAllKeys = new HashSet<string>(systemChecks.Select(i => i.Key));
var systemCheckSelectedKeysHashSet = new HashSet<string>();
var systemCheckAllKeys = new HashSet<string>(_systemChecks.Select(i => i.Key));
foreach (string arg in args)
{
@ -114,11 +115,13 @@ namespace winPEAS.Checks
string argToLower = arg.ToLower();
if (systemCheckAllKeys.Contains(argToLower))
{
systemCheckSelectedKeysHashSet.Add(argToLower);
_systemCheckSelectedKeysHashSet.Add(argToLower);
isAllChecks = false;
}
}
CheckRunner.Run(() =>
{
//Start execution
if (IsNoColor)
{
@ -133,15 +136,21 @@ namespace winPEAS.Checks
Beaprint.PrintInit(IsDebug);
for (int i = 0; i < systemChecks.Count; i++)
{
var systemCheck = systemChecks[i];
RunChecks(isAllChecks, wait);
}, IsDebug, "Total time");
}
if (systemCheckSelectedKeysHashSet.Contains(systemCheck.Key) || isAllChecks)
private static void RunChecks(bool isAllChecks, bool wait)
{
for (int i = 0; i < _systemChecks.Count; i++)
{
var systemCheck = _systemChecks[i];
if (_systemCheckSelectedKeysHashSet.Contains(systemCheck.Key) || isAllChecks)
{
systemCheck.Check.PrintInfo(IsDebug);
if ((i < systemCheckSelectedKeysHashSet.Count - 1) && wait)
if ((i < _systemCheckSelectedKeysHashSet.Count - 1) && wait)
{
WaitInput();
}

View File

@ -5,7 +5,7 @@ namespace winPEAS.Helpers
{
internal static class CheckRunner
{
public static void Run(Action action, bool isDebug)
public static void Run(Action action, bool isDebug, string description = null)
{
var timer = new Stopwatch();
@ -21,7 +21,8 @@ namespace winPEAS.Helpers
timer.Stop();
TimeSpan timeTaken = timer.Elapsed;
string log = $"Execution took : {timeTaken.Minutes:00}m:{timeTaken.Seconds:00}s:{timeTaken.Milliseconds:000}";
string descriptionText = string.IsNullOrEmpty(description) ? string.Empty : $"[{description}] ";
string log = $"{descriptionText}Execution took : {timeTaken.Minutes:00}m:{timeTaken.Seconds:00}s:{timeTaken.Milliseconds:000}";
Beaprint.PrintDebugLine(log);
}

View File

@ -34,27 +34,27 @@ namespace winPEAS.Info.ApplicationInfo
{
var results = new List<Dictionary<string, string>>();
try
void ProcessTaskFolder(TaskFolder taskFolder)
{
void EnumFolderTasks(TaskFolder fld)
foreach (var runTask in taskFolder.GetTasks()) // browse all tasks in folder
{
foreach (Task task in fld.Tasks)
{
ActOnTask(task);
ActOnTask(runTask);
}
//task.Name
//task.Enabled
//task.Definition.Actions
//task.Definition
foreach (TaskFolder sfld in fld.SubFolders)
foreach (var taskFolderSub in taskFolder.SubFolders) // recursively browse subfolders
{
EnumFolderTasks(sfld);
ProcessTaskFolder(taskFolderSub);
}
}
void ActOnTask(Task t)
{
if (t.Enabled && (!string.IsNullOrEmpty(t.Definition.RegistrationInfo.Author) && !t.Definition.RegistrationInfo.Author.Contains("Microsoft")))
try
{
if (t.Enabled &&
!string.IsNullOrEmpty(t.Path) && !t.Path.Contains("Microsoft") &&
!string.IsNullOrEmpty(t.Definition.RegistrationInfo.Author) &&
!t.Definition.RegistrationInfo.Author.Contains("Microsoft"))
{
List<string> f_trigger = new List<string>();
foreach (Trigger trigger in t.Definition.Triggers)
@ -72,12 +72,16 @@ namespace winPEAS.Info.ApplicationInfo
});
}
}
EnumFolderTasks(TaskService.Instance.RootFolder);
}
catch (Exception ex)
{
Beaprint.GrayPrint("Error: " + ex);
Beaprint.PrintException($"failed to process scheduled task: '{t.Name}': {ex.Message}");
}
}
TaskFolder folder = TaskService.Instance.GetFolder("\\");
ProcessTaskFolder(folder);
return results;
}
}

View File

@ -48,18 +48,18 @@ namespace winPEAS.Info.ServicesInfo
if (string.IsNullOrEmpty(companyName) || (!Regex.IsMatch(companyName, @"^Microsoft.*", RegexOptions.IgnoreCase)))
{
Dictionary<string, string> toadd = new Dictionary<string, string>
{
["Name"] = string.Format("{0}", result["Name"]),
["DisplayName"] = string.Format("{0}", result["DisplayName"]),
["CompanyName"] = companyName,
["State"] = string.Format("{0}", result["State"]),
["StartMode"] = string.Format("{0}", result["StartMode"]),
["PathName"] = string.Format("{0}", result["PathName"]),
["FilteredPath"] = binaryPath,
["isDotNet"] = isDotNet,
["Description"] = result["Description"].ToString()
};
Dictionary<string, string> toadd = new Dictionary<string, string>();
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"]);
results.Add(toadd);
}
}
@ -72,6 +72,11 @@ namespace winPEAS.Info.ServicesInfo
return results;
}
private static string GetStringOrEmpty(object obj)
{
return obj == null ? string.Empty : obj.ToString();
}
public static List<Dictionary<string, string>> GetNonstandardServicesFromReg()
{
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
@ -234,7 +239,7 @@ namespace winPEAS.Info.ServicesInfo
}
}
catch (Exception)
catch (Exception ex)
{
//Beaprint.PrintException(ex.Message)
}

View File

@ -158,6 +158,8 @@ namespace winPEAS.Info.UserInfo
SelectQuery query = new SelectQuery("Win32_UserProfile");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject user in searcher.Get())
{
try
{
string username = new SecurityIdentifier(user["SID"].ToString()).Translate(typeof(NTAccount)).ToString();
if (!username.Contains("NT AUTHORITY"))
@ -165,6 +167,11 @@ namespace winPEAS.Info.UserInfo
retList.Add(username);
}
}
// user SID could not be translated, ignore
catch (Exception)
{
}
}
}
catch (Exception ex)
{

View File

@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler.Native
namespace winPEAS.TaskScheduler
{
/// <summary>Extensions for classes in the System.Security.AccessControl namespace.</summary>
public static class AccessControlExtension

File diff suppressed because it is too large Load Diff

View File

@ -1,64 +1,68 @@
using System;
using System.Collections;
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.Native;
using winPEAS.TaskScheduler.V1;
using winPEAS.TaskScheduler.V2;
using winPEAS.TaskScheduler.TaskEditor.Native;
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Options for when to convert actions to PowerShell equivalents.
/// </summary>
/// <summary>Options for when to convert actions to PowerShell equivalents.</summary>
[Flags]
public enum PowerShellActionPlatformOption
{
/// <summary>
/// Never convert any actions to PowerShell. This will force exceptions to be thrown when unsupported actions our action quantities are found.
/// Never convert any actions to PowerShell. This will force exceptions to be thrown when unsupported actions our action quantities
/// are found.
/// </summary>
Never = 0,
/// <summary>
/// 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 <see cref="ExecAction"/> is supported. Developer must ensure that PowerShell v2 or higher is installed on the target computer.
/// 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 <see cref="ExecAction"/> is supported. Developer must ensure that
/// PowerShell v2 or higher is installed on the target computer.
/// </summary>
Version1 = 1,
/// <summary>
/// Convert all <see cref="ShowMessageAction"/> and <see cref="EmailAction"/> references to their PowerShell equivalents on systems on or after Windows 8 / Server 2012.
/// Convert all <see cref="ShowMessageAction"/> and <see cref="EmailAction"/> references to their PowerShell equivalents on systems
/// on or after Windows 8 / Server 2012.
/// </summary>
Version2 = 2,
/// <summary>
/// Convert all actions regardless of version or operating system.
/// </summary>
/// <summary>Convert all actions regardless of version or operating system.</summary>
All = 3
}
/// <summary>
/// Collection that contains the actions that are performed by the task.
/// </summary>
/// <summary>Collection that contains the actions that are performed by the task.</summary>
[XmlRoot("Actions", Namespace = TaskDefinition.tns, IsNullable = false)]
[PublicAPI]
public sealed class ActionCollection : IList<Action>, IDisposable, IXmlSerializable, IList
public sealed class ActionCollection : IList<Action>, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
internal const int MaxActions = 32;
private List<Action> v1Actions;
private ITask v1Task;
private IActionCollection v2Coll;
private ITaskDefinition v2Def;
private PowerShellActionPlatformOption psConvert = PowerShellActionPlatformOption.Version2;
private const string IndexerName = "Item[]";
private static readonly string psV2IdRegex = $"(?:; )?{nameof(PowerShellConversion)}=(?<v>0|1)";
private bool inV2set;
private PowerShellActionPlatformOption psConvert = PowerShellActionPlatformOption.Version2;
private readonly List<Action> v1Actions;
private V1Interop.ITask v1Task;
private readonly V2Interop.IActionCollection v2Coll;
private V2Interop.ITaskDefinition v2Def;
internal ActionCollection([NotNull] ITask task)
internal ActionCollection([NotNull] V1Interop.ITask task)
{
v1Task = task;
v1Actions = GetV1Actions();
PowerShellConversion = Action.TryParse(v1Task.GetDataItem(nameof(PowerShellConversion)), psConvert | PowerShellActionPlatformOption.Version2);
}
internal ActionCollection([NotNull] ITaskDefinition iTaskDef)
internal ActionCollection([NotNull] V2Interop.ITaskDefinition iTaskDef)
{
v2Def = iTaskDef;
v2Coll = iTaskDef.Actions;
@ -75,9 +79,13 @@ namespace winPEAS.TaskScheduler
UnconvertUnsupportedActions();
}
/// <summary>
/// Gets or sets the identifier of the principal for the task.
/// </summary>
/// <summary>Occurs when a collection changes.</summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>Occurs when a property value changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Gets or sets the identifier of the principal for the task.</summary>
[XmlAttribute]
[DefaultValue(null)]
[CanBeNull]
@ -95,36 +103,40 @@ namespace winPEAS.TaskScheduler
v2Coll.Context = value;
else
v1Task.SetDataItem("ActionCollectionContext", value);
OnNotifyPropertyChanged();
}
}
/// <summary>
/// Gets the number of actions in the collection.
/// Gets or sets the systems under which unsupported actions will be converted to PowerShell <see cref="ExecAction"/> instances.
/// </summary>
public int Count => (v2Coll != null) ? v2Coll.Count : v1Actions.Count;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => this;
bool ICollection<Action>.IsReadOnly => false;
bool IList.IsFixedSize => false;
bool IList.IsReadOnly => false;
/// <summary>Gets or sets the systems under which unsupported actions will be converted to PowerShell <see cref="ExecAction"/> instances.</summary>
/// <value>The PowerShell platform options.</value>
/// <remarks>This property will affect how many actions are physically stored in the system and is tied to the version of Task Scheduler.
/// <para>If set to <see cref="PowerShellActionPlatformOption.Never"/>, then no actions will ever be converted to PowerShell. This will force exceptions to be thrown when unsupported actions our action quantities are found.</para>
/// <para>If set to <see cref="PowerShellActionPlatformOption.Version1"/>, 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 <see cref="ExecAction"/> is supported. Developer must ensure that PowerShell v2 or higher is installed on the target computer.</para>
/// <para>If set to <see cref="PowerShellActionPlatformOption.Version2"/> (which is the default value), then <see cref="ShowMessageAction"/> and <see cref="EmailAction"/> references will be converted to their PowerShell equivalents on systems on or after Windows 8 / Server 2012.</para>
/// <para>If set to <see cref="PowerShellActionPlatformOption.All"/>, then any actions not supported by the Task Scheduler version will be converted to PowerShell.</para>
/// <remarks>
/// This property will affect how many actions are physically stored in the system and is tied to the version of Task Scheduler.
/// <para>
/// If set to <see cref="PowerShellActionPlatformOption.Never"/>, then no actions will ever be converted to PowerShell. This will
/// force exceptions to be thrown when unsupported actions our action quantities are found.
/// </para>
/// <para>
/// If set to <see cref="PowerShellActionPlatformOption.Version1"/>, 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 <see cref="ExecAction"/> is supported. Developer must ensure that PowerShell v2 or higher is installed on the
/// target computer.
/// </para>
/// <para>
/// If set to <see cref="PowerShellActionPlatformOption.Version2"/> (which is the default value), then <see
/// cref="ShowMessageAction"/> and <see cref="EmailAction"/> references will be converted to their PowerShell equivalents on systems
/// on or after Windows 8 / Server 2012.
/// </para>
/// <para>
/// If set to <see cref="PowerShellActionPlatformOption.All"/>, then any actions not supported by the Task Scheduler version will be
/// converted to PowerShell.
/// </para>
/// </remarks>
[DefaultValue(typeof(PowerShellActionPlatformOption), "Version2")]
public PowerShellActionPlatformOption PowerShellConversion
{
get { return psConvert; }
get => psConvert;
set
{
if (psConvert != value)
@ -139,23 +151,79 @@ namespace winPEAS.TaskScheduler
if (!SupportV2Conversion)
v2Def.Data = string.Format("{0}; {1}=0", v2Def.Data, nameof(PowerShellConversion));
}
OnNotifyPropertyChanged();
}
}
}
/// <summary>Gets or sets an XML-formatted version of the collection.</summary>
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();
}
}
/// <summary>Gets the number of actions in the collection.</summary>
public int Count => (v2Coll != null) ? v2Coll.Count : v1Actions.Count;
bool IList.IsFixedSize => false;
bool ICollection<Action>.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;
object IList.this[int index]
/// <summary>Gets or sets a specified action from the collection.</summary>
/// <value>The <see cref="Action"/>.</value>
/// <param name="actionId">The id ( <see cref="Action.Id"/>) of the action to be retrieved.</param>
/// <returns>Specialized <see cref="Action"/> instance.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="NullReferenceException"></exception>
/// <exception cref="InvalidOperationException">Mismatching Id for action and lookup.</exception>
[NotNull]
public Action this[string actionId]
{
get { return this[index]; }
set { this[index] = (Action)value; }
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);
}
}
/// <summary>
/// Gets or sets a an action at the specified index.
/// </summary>
/// <summary>Gets or sets a an action at the specified index.</summary>
/// <value>The zero-based index of the action to get or set.</value>
[NotNull]
public Action this[int index]
@ -178,24 +246,39 @@ namespace winPEAS.TaskScheduler
}
set
{
if (Count <= index)
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));
}
}
/// <summary>
/// Adds an action to the task.
/// </summary>
object IList.this[int index]
{
get => this[index];
set => this[index] = (Action)value;
}
/// <summary>Adds an action to the task.</summary>
/// <typeparam name="TAction">A type derived from <see cref="Action"/>.</typeparam>
/// <param name="action">A derived <see cref="Action"/> class.</param>
/// <returns>The bound <see cref="Action"/> that was added to the collection.</returns>
@ -213,12 +296,24 @@ namespace winPEAS.TaskScheduler
v1Actions.Add(action);
SaveV1Actions();
}
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action));
return action;
}
/// <summary>
/// Adds a new <see cref="Action"/> instance to the task.
/// </summary>
/// <summary>Adds an <see cref="ExecAction"/> to the task.</summary>
/// <param name="path">Path to an executable file.</param>
/// <param name="arguments">Arguments associated with the command-line operation. This value can be null.</param>
/// <param name="workingDirectory">
/// Directory that contains either the executable file or the files that are used by the executable file. This value can be null.
/// </param>
/// <returns>The bound <see cref="ExecAction"/> that was added to the collection.</returns>
[NotNull]
public ExecAction Add([NotNull] string path, [CanBeNull] string arguments = null, [CanBeNull] string workingDirectory = null) =>
Add(new ExecAction(path, arguments, workingDirectory));
/// <summary>Adds a new <see cref="Action"/> instance to the task.</summary>
/// <param name="actionType">Type of task to be created</param>
/// <returns>Specialized <see cref="Action"/> instance.</returns>
[NotNull]
@ -235,9 +330,33 @@ namespace winPEAS.TaskScheduler
return Action.CreateAction(v2Coll.Create(actionType));
}
/// <summary>
/// Clears all actions from the task.
/// </summary>
/// <summary>Adds a collection of actions to the end of the <see cref="ActionCollection"/>.</summary>
/// <param name="actions">
/// The actions to be added to the end of the <see cref="ActionCollection"/>. The collection itself cannot be <c>null</c> and cannot
/// contain <c>null</c> elements.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="actions"/> is <c>null</c>.</exception>
public void AddRange([ItemNotNull, NotNull] IEnumerable<Action> actions)
{
if (actions == null)
throw new ArgumentNullException(nameof(actions));
if (v1Task != null)
{
var list = new List<Action>(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(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);
}
}
/// <summary>Clears all actions from the task.</summary>
public void Clear()
{
if (v2Coll != null)
@ -247,37 +366,48 @@ namespace winPEAS.TaskScheduler
v1Actions.Clear();
SaveV1Actions();
}
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
/// </summary>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/// <returns>
/// true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.
/// </returns>
/// <summary>Determines whether the <see cref="ICollection{T}"/> contains a specific value.</summary>
/// <param name="item">The object to locate in the <see cref="ICollection{T}"/>.</param>
/// <returns>true if <paramref name="item"/> is found in the <see cref="ICollection{T}"/>; otherwise, false.</returns>
public bool Contains([NotNull] Action item) => Find(a => a.Equals(item)) != null;
/// <summary>Determines whether the specified action type is contained in this collection.</summary>
/// <param name="actionType">Type of the action.</param>
/// <returns><c>true</c> if the specified action type is contained in this collection; otherwise, <c>false</c>.</returns>
public bool ContainsType(Type actionType) => Find(a => a.GetType() == actionType) != null;
/// <summary>
/// Copies the elements of the <see cref="ActionCollection"/> to an array of <see cref="Action"/>, starting at a particular index.
/// </summary>
/// <param name="array">The <see cref="Action" /> array that is the destination of the elements copied from <see cref="ActionCollection" />. The <see cref="Action" /> array must have zero-based indexing.</param>
/// <param name="array">
/// The <see cref="Action"/> array that is the destination of the elements copied from <see cref="ActionCollection"/>. The <see
/// cref="Action"/> array must have zero-based indexing.
/// </param>
/// <param name="arrayIndex">The zero-based index in <see cref="Action"/> array at which copying begins.</param>
public void CopyTo(Action[] array, int arrayIndex)
{
CopyTo(0, array, arrayIndex, Count);
}
public void CopyTo(Action[] array, int arrayIndex) => CopyTo(0, array, arrayIndex, Count);
/// <summary>
/// Copies the elements of the <see cref="ActionCollection" /> to an <see cref="Action" /> array, starting at a particular <see cref="Action" /> array index.
/// Copies the elements of the <see cref="ActionCollection"/> to an <see cref="Action"/> array, starting at a particular <see
/// cref="Action"/> array index.
/// </summary>
/// <param name="index">The zero-based index in the source at which copying begins.</param>
/// <param name="array">The <see cref="Action" /> array that is the destination of the elements copied from <see cref="ActionCollection" />. The <see cref="Action" /> array must have zero-based indexing.</param>
/// <param name="array">
/// The <see cref="Action"/> array that is the destination of the elements copied from <see cref="ActionCollection"/>. The <see
/// cref="Action"/> array must have zero-based indexing.
/// </param>
/// <param name="arrayIndex">The zero-based index in <see cref="Action"/> array at which copying begins.</param>
/// <param name="count">The number of elements to copy.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="array" /> is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException"><paramref name="arrayIndex" /> is less than 0.</exception>
/// <exception cref="System.ArgumentException">The number of elements in the source <see cref="ActionCollection" /> is greater than the available space from <paramref name="arrayIndex" /> to the end of the destination <paramref name="array" />.</exception>
/// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception>
/// <exception cref="ArgumentException">
/// The number of elements in the source <see cref="ActionCollection"/> is greater than the available space from <paramref
/// name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
/// </exception>
public void CopyTo(int index, [NotNull] Action[] array, int arrayIndex, int count)
{
if (array == null)
@ -290,13 +420,11 @@ namespace winPEAS.TaskScheduler
throw new ArgumentOutOfRangeException(nameof(count));
if ((Count - index) > (array.Length - arrayIndex))
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
for (int i = 0; i < count; i++)
for (var i = 0; i < count; i++)
array[arrayIndex + i] = (Action)this[index + i].Clone();
}
/// <summary>
/// Releases all resources used by this class.
/// </summary>
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
v1Task = null;
@ -305,10 +433,15 @@ namespace winPEAS.TaskScheduler
}
/// <summary>
/// Searches for an <see cref="Action"/> that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire collection.
/// Searches for an <see cref="Action"/> that matches the conditions defined by the specified predicate, and returns the first
/// occurrence within the entire collection.
/// </summary>
/// <param name="match">The <see cref="Predicate{Action}"/> delegate that defines the conditions of the <see cref="Action"/> to search for.</param>
/// <returns>The first <see cref="Action"/> that matches the conditions defined by the specified predicate, if found; otherwise, <c>null</c>.</returns>
/// <param name="match">
/// The <see cref="Predicate{Action}"/> delegate that defines the conditions of the <see cref="Action"/> to search for.
/// </param>
/// <returns>
/// The first <see cref="Action"/> that matches the conditions defined by the specified predicate, if found; otherwise, <c>null</c>.
/// </returns>
public Action Find(Predicate<Action> match)
{
if (match == null)
@ -319,12 +452,15 @@ namespace winPEAS.TaskScheduler
}
/// <summary>
/// Searches for an <see cref="Action"/> 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.
/// Searches for an <see cref="Action"/> 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.
/// </summary>
/// <param name="startIndex">The zero-based starting index of the search.</param>
/// <param name="count">The number of elements in the collection to search.</param>
/// <param name="match">The <see cref="Predicate{Action}"/> delegate that defines the conditions of the element to search for.</param>
/// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.</returns>
/// <returns>
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.
/// </returns>
public int FindIndexOf(int startIndex, int count, [NotNull] Predicate<Action> match)
{
if (startIndex < 0 || startIndex >= Count)
@ -333,77 +469,49 @@ namespace winPEAS.TaskScheduler
throw new ArgumentOutOfRangeException(nameof(count));
if (match == null)
throw new ArgumentNullException(nameof(match));
for (int i = startIndex; i < startIndex + count; i++)
for (var i = startIndex; i < startIndex + count; i++)
if (match(this[i])) return i;
return -1;
}
/// <summary>
/// Searches for an <see cref="Action"/> that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the collection.
/// Searches for an <see cref="Action"/> that matches the conditions defined by the specified predicate, and returns the zero-based
/// index of the first occurrence within the collection.
/// </summary>
/// <param name="match">The <see cref="Predicate{Action}"/> delegate that defines the conditions of the element to search for.</param>
/// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.</returns>
/// <returns>
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.
/// </returns>
public int FindIndexOf([NotNull] Predicate<Action> match) => FindIndexOf(0, Count, match);
/// <summary>
/// Retrieves an enumeration of each of the actions.
/// </summary>
/// <returns>Returns an object that implements the <see cref="IEnumerator"/> interface and that can iterate through the <see cref="Action"/> objects within the <see cref="ActionCollection"/>.</returns>
/// <summary>Retrieves an enumeration of each of the actions.</summary>
/// <returns>
/// Returns an object that implements the <see cref="IEnumerator"/> interface and that can iterate through the <see cref="Action"/>
/// objects within the <see cref="ActionCollection"/>.
/// </returns>
public IEnumerator<Action> GetEnumerator()
{
if (v2Coll != null)
return new ComEnumerator<Action, IAction>(() => v2Coll.Count, i => v2Coll[i], Action.CreateAction);
return new ComEnumerator<Action, V2Interop.IAction>(() => v2Coll.Count, i => v2Coll[i], Action.CreateAction);
return v1Actions.GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
if (array != null && array.Rank != 1)
throw new RankException("Multi-dimensional arrays are not supported.");
Action[] src = new Action[Count];
CopyTo(src, 0);
Array.Copy(src, 0, array, index, Count);
}
void ICollection<Action>.Add(Action item)
{
Add(item);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
int IList.Add(object value)
{
Add((Action)value);
return Count - 1;
}
bool IList.Contains(object value) => Contains((Action)value);
int IList.IndexOf(object value) => IndexOf((Action)value);
void IList.Insert(int index, object value)
{
Insert(index, (Action)value);
}
void IList.Remove(object value)
{
Remove((Action)value);
}
/// <summary>
/// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1" />.
/// </summary>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1" />.</param>
/// <returns>
/// The index of <paramref name="item" /> if found in the list; otherwise, -1.
/// </returns>
/// <summary>Determines the index of a specific item in the <see cref="IList{T}"/>.</summary>
/// <param name="item">The object to locate in the <see cref="IList{T}"/>.</param>
/// <returns>The index of <paramref name="item"/> if found in the list; otherwise, -1.</returns>
public int IndexOf(Action item) => FindIndexOf(a => a.Equals(item));
/// <summary>
/// Inserts an action at the specified index.
/// </summary>
/// <summary>Determines the index of a specific item in the <see cref="IList{T}"/>.</summary>
/// <param name="actionId">The id ( <see cref="Action.Id"/>) of the action to be retrieved.</param>
/// <returns>The index of <paramref name="actionId"/> if found in the list; otherwise, -1.</returns>
public int IndexOf(string actionId)
{
if (string.IsNullOrEmpty(actionId))
throw new ArgumentNullException(nameof(actionId));
return FindIndexOf(a => string.Equals(a.Id, actionId));
}
/// <summary>Inserts an action at the specified index.</summary>
/// <param name="index">The zero-based index at which action should be inserted.</param>
/// <param name="action">The action to insert into the list.</param>
public void Insert(int index, [NotNull] Action action)
@ -415,16 +523,16 @@ namespace winPEAS.TaskScheduler
if (v2Coll != null)
{
Action[] pushItems = new Action[Count - index];
var pushItems = new Action[Count - index];
if (Count != index)
{
CopyTo(index, pushItems, 0, Count - index);
for (int j = Count - 1; j >= index; j--)
for (var j = Count - 1; j >= index; j--)
RemoveAt(j);
}
Add(action);
if (Count != index)
for (int k = 0; k < pushItems.Length; k++)
for (var k = 0; k < pushItems.Length; k++)
Add(pushItems[k]);
}
else
@ -434,40 +542,24 @@ namespace winPEAS.TaskScheduler
v1Actions.Insert(index, action);
SaveV1Actions();
}
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null;
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
if (!inV2set)
{
reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns);
Context = reader.GetAttribute("Context");
while (reader.MoveToContent() == System.Xml.XmlNodeType.Element)
{
Action a = Action.CreateAction(Action.TryParse(reader.LocalName == "Exec" ? "Execute" : reader.LocalName, TaskActionType.Execute));
XmlSerializationHelper.ReadObject(reader, a);
this.Add(a);
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action, index));
}
reader.ReadEndElement();
}
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);
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/// <summary>Removes the first occurrence of a specific object from the <see cref="ICollection{T}"/>.</summary>
/// <param name="item">The object to remove from the <see cref="ICollection{T}"/>.</param>
/// <returns>
/// true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
/// true if <paramref name="item"/> was successfully removed from the <see cref="ICollection{T}"/>; otherwise, false. This method
/// also returns false if <paramref name="item"/> is not found in the original <see cref="ICollection{T}"/>.
/// </returns>
public bool Remove([NotNull] Action item)
{
int idx = IndexOf(item);
var idx = IndexOf(item);
if (idx != -1)
{
try
@ -480,15 +572,14 @@ namespace winPEAS.TaskScheduler
return false;
}
/// <summary>
/// Removes the action at a specified index.
/// </summary>
/// <summary>Removes the action at a specified index.</summary>
/// <param name="index">Index of action to remove.</param>
/// <exception cref="ArgumentOutOfRangeException">Index out of range.</exception>
public void RemoveAt(int index)
{
if (index >= Count)
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
@ -496,30 +587,91 @@ namespace winPEAS.TaskScheduler
v1Actions.RemoveAt(index);
SaveV1Actions();
}
if (!inV2set)
{
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the actions in this collection.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents the actions in this collection.
/// </returns>
/// <summary>Copies the elements of the <see cref="ActionCollection"/> to a new array.</summary>
/// <returns>An array containing copies of the elements of the <see cref="ActionCollection"/>.</returns>
[NotNull, ItemNotNull]
public Action[] ToArray()
{
var ret = new Action[Count];
CopyTo(ret, 0);
return ret;
}
/// <summary>Returns a <see cref="string"/> that represents the actions in this collection.</summary>
/// <returns>A <see cref="string"/> that represents the actions in this collection.</returns>
public override string ToString()
{
if (Count == 1)
return this[0].ToString();
if (Count > 1)
return winPEAS.Properties.Resources.MultipleActions;
return Properties.Resources.MultipleActions;
return string.Empty;
}
void ICollection<Action>.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 (int i = 0; i < Count; i++)
for (var i = 0; i < Count; i++)
{
Action action = this[i];
var action = this[i];
var bindable = action as IBindAsExecAction;
if (bindable != null && !(action is ComHandlerAction))
this[i] = ExecAction.ConvertToPowerShellAction(action);
@ -527,26 +679,9 @@ namespace winPEAS.TaskScheduler
}
}
private void UnconvertUnsupportedActions()
{
if (TaskService.LibraryVersion.Minor > 3)
{
for (int i = 0; i < Count; i++)
{
ExecAction action = this[i] as ExecAction;
if (action != null)
{
Action newAction = Action.ConvertFromPowerShellAction(action);
if (newAction != null)
this[i] = newAction;
}
}
}
}
private List<Action> GetV1Actions()
{
List<Action> ret = new List<Action>();
var ret = new List<Action>();
if (v1Task != null && v1Task.GetDataItem("ActionType") != "EMPTY")
{
var exec = new ExecAction(v1Task);
@ -579,6 +714,10 @@ namespace winPEAS.TaskScheduler
return ret;
}
/// <summary>Called when a property has changed to notify any attached elements.</summary>
/// <param name="propertyName">Name of the property.</param>
private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void SaveV1Actions()
{
if (v1Task == null)
@ -614,5 +753,22 @@ namespace winPEAS.TaskScheduler
v1Task.SetDataItem("ActionType", "MULTIPLE");
}
}
private void UnconvertUnsupportedActions()
{
if (TaskService.LibraryVersion.Minor > 3)
{
for (var i = 0; i < Count; i++)
{
var action = this[i] as ExecAction;
if (action != null)
{
var newAction = Action.ConvertFromPowerShellAction(action);
if (newAction != null)
this[i] = newAction;
}
}
}
}
}
}

View File

@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{
@ -7,6 +11,13 @@ namespace winPEAS.TaskScheduler
{
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 void Dispose()
{
Thread.CurrentThread.CurrentCulture = cur;

View File

@ -37,7 +37,7 @@ namespace winPEAS.TaskScheduler
private static string GetCultureEquivalentString(DaysOfTheWeek val)
{
if (val == DaysOfTheWeek.AllDays)
return winPEAS.Properties.Resources.DOWAllDays;
return Properties.Resources.DOWAllDays;
var s = new List<string>(7);
var vals = Enum.GetValues(val.GetType());
@ -47,13 +47,13 @@ namespace winPEAS.TaskScheduler
s.Add(DateTimeFormatInfo.CurrentInfo.GetDayName((DayOfWeek)i));
}
return string.Join(winPEAS.Properties.Resources.ListSeparator, s.ToArray());
return string.Join(Properties.Resources.ListSeparator, s.ToArray());
}
private static string GetCultureEquivalentString(MonthsOfTheYear val)
{
if (val == MonthsOfTheYear.AllMonths)
return winPEAS.Properties.Resources.MOYAllMonths;
return Properties.Resources.MOYAllMonths;
var s = new List<string>(12);
var vals = Enum.GetValues(val.GetType());
@ -63,7 +63,7 @@ namespace winPEAS.TaskScheduler
s.Add(DateTimeFormatInfo.CurrentInfo.GetMonthName(i + 1));
}
return string.Join(winPEAS.Properties.Resources.ListSeparator, s.ToArray());
return string.Join(Properties.Resources.ListSeparator, s.ToArray());
}
private static string BuildEnumString(string preface, object enumValue)
@ -72,8 +72,8 @@ namespace winPEAS.TaskScheduler
if (vals.Length == 0)
return string.Empty;
for (var i = 0; i < vals.Length; i++)
vals[i] = winPEAS.Properties.Resources.ResourceManager.GetString(preface + vals[i]);
return string.Join(winPEAS.Properties.Resources.ListSeparator, vals);
vals[i] = Properties.Resources.ResourceManager.GetString(preface + vals[i]);
return string.Join(Properties.Resources.ListSeparator, vals);
}
}
}

View File

@ -0,0 +1,150 @@
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<T>(bool checkHasFlags = false)
{
if (!typeof(T).IsEnum)
throw new ArgumentException($"Type '{typeof(T).FullName}' is not an enum");
if (checkHasFlags && !IsFlags<T>())
throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute");
}
public static bool IsFlags<T>() => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute));
public static void CheckHasValue<T>(T value, string argName = null)
{
CheckIsEnum<T>();
if (IsFlags<T>())
{
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<T>(this T flags) where T : struct, IConvertible
{
CheckIsEnum<T>(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<T>(this T flags, T flag) where T : struct, IConvertible
{
CheckIsEnum<T>(true);
var flagValue = Convert.ToInt64(flag);
return (Convert.ToInt64(flags) & flagValue) == flagValue;
}
public static bool IsValidFlagValue<T>(this T flags) where T : struct, IConvertible
{
CheckIsEnum<T>(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<T>(ref T flags, T flag, bool set = true) where T : struct, IConvertible
{
CheckIsEnum<T>(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<T>(this T flags, T flag, bool set = true) where T : struct, IConvertible
{
var ret = flags;
SetFlags<T>(ref ret, flag, set);
return ret;
}
public static T ClearFlags<T>(this T flags, T flag) where T : struct, IConvertible => flags.SetFlags(flag, false);
public static IEnumerable<T> GetFlags<T>(this T value) where T : struct, IConvertible
{
CheckIsEnum<T>(true);
foreach (T flag in Enum.GetValues(typeof(T)))
{
if (value.IsFlagSet(flag))
yield return flag;
}
}
public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct, IConvertible
{
CheckIsEnum<T>(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<T>(this T value) where T : struct, IConvertible
{
CheckIsEnum<T>();
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;
}
/// <summary>
/// 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 <paramref name="defaultVal"/>. If <paramref name="defaultVal"/> is undefined, it returns the first declared item in the enumerated type.
/// </summary>
/// <typeparam name="TEnum">The enumeration type to which to convert <paramref name="value"/>.</typeparam>
/// <param name="value">The string representation of the enumeration name or underlying value to convert.</param>
/// <param name="ignoreCase"><c>true</c> to ignore case; <c>false</c> to consider case.</param>
/// <param name="defaultVal">The default value.</param>
/// <returns>An object of type <typeparamref name="TEnum"/> whose value is represented by value.</returns>
public static TEnum TryParse<TEnum>(string value, bool ignoreCase = false, TEnum defaultVal = default(TEnum)) where TEnum : struct, IConvertible
{
CheckIsEnum<TEnum>();
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;
}
}
}

View File

@ -1,34 +1,8 @@
/* MIT License
Copyright (c) 2016 JetBrains http://www.jetbrains.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */
using System;
#pragma warning disable 1591
// ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable IntroduceOptionalParameters.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable InconsistentNaming
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{
@ -74,6 +48,231 @@ namespace winPEAS.TaskScheduler
AttributeTargets.Delegate | AttributeTargets.Field)]
internal sealed class ItemNotNullAttribute : Attribute { }
/// <summary>
/// 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.
/// </summary>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field)]
internal sealed class ItemCanBeNullAttribute : Attribute { }
/// <summary>
/// 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 <see cref="string.Format(IFormatProvider,string,object[])"/>-like form.
/// </summary>
/// <example><code>
/// [StringFormatMethod("message")]
/// void ShowError(string message, params object[] args) { /* do something */ }
///
/// void Foo() {
/// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Constructor | AttributeTargets.Method |
AttributeTargets.Property | AttributeTargets.Delegate)]
internal sealed class StringFormatMethodAttribute : Attribute
{
/// <param name="formatParameterName">
/// Specifies which parameter of an annotated method should be treated as format-string
/// </param>
public StringFormatMethodAttribute([NotNull] string formatParameterName)
{
FormatParameterName = formatParameterName;
}
[NotNull] public string FormatParameterName { get; private set; }
}
/// <summary>
/// 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.
/// </summary>
[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; }
}
/// <summary>
/// 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 <see cref="System.ArgumentNullException"/>.
/// </summary>
/// <example><code>
/// void Foo(string param) {
/// if (param == null)
/// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class InvokerParameterNameAttribute : Attribute { }
/// <summary>
/// Indicates that the method is contained in a type that implements
/// <c>System.ComponentModel.INotifyPropertyChanged</c> interface and this method
/// is used to notify that some property value changed.
/// </summary>
/// <remarks>
/// The method should be non-static and conform to one of the supported signatures:
/// <list>
/// <item><c>NotifyChanged(string)</c></item>
/// <item><c>NotifyChanged(params string[])</c></item>
/// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>
/// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>
/// <item><c>SetProperty{T}(ref T, T, string)</c></item>
/// </list>
/// </remarks>
/// <example><code>
/// 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 */ }
/// }
/// }
/// </code>
/// Examples of generated notifications:
/// <list>
/// <item><c>NotifyChanged("Property")</c></item>
/// <item><c>NotifyChanged(() =&gt; Property)</c></item>
/// <item><c>NotifyChanged((VM x) =&gt; x.Property)</c></item>
/// <item><c>SetProperty(ref myField, value, "Property")</c></item>
/// </list>
/// </example>
[AttributeUsage(AttributeTargets.Method)]
internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute
{
public NotifyPropertyChangedInvocatorAttribute() { }
public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName)
{
ParameterName = parameterName;
}
[CanBeNull] public string ParameterName { get; private set; }
}
/// <summary>
/// Describes dependency between method input and output.
/// </summary>
/// <syntax>
/// <p>Function Definition Table syntax:</p>
/// <list>
/// <item>FDT ::= FDTRow [;FDTRow]*</item>
/// <item>FDTRow ::= Input =&gt; Output | Output &lt;= Input</item>
/// <item>Input ::= ParameterName: Value [, Input]*</item>
/// <item>Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>
/// <item>Value ::= true | false | null | notnull | canbenull</item>
/// </list>
/// If method has single input parameter, it's name could be omitted.<br/>
/// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same) for method output
/// means that the methos doesn't return normally (throws or terminates the process).<br/>
/// Value <c>canbenull</c> is only applicable for output parameters.<br/>
/// You can use multiple <c>[ContractAnnotation]</c> 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.<br/>
/// </syntax>
/// <examples><list>
/// <item><code>
/// [ContractAnnotation("=&gt; halt")]
/// public void TerminationMethod()
/// </code></item>
/// <item><code>
/// [ContractAnnotation("halt &lt;= condition: false")]
/// public void Assert(bool condition, string text) // regular assertion method
/// </code></item>
/// <item><code>
/// [ContractAnnotation("s:null =&gt; true")]
/// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
/// </code></item>
/// <item><code>
/// // A method that returns null if the parameter is null,
/// // and not null if the parameter is not null
/// [ContractAnnotation("null =&gt; null; notnull =&gt; notnull")]
/// public object Transform(object data)
/// </code></item>
/// <item><code>
/// [ContractAnnotation("=&gt; true, result: notnull; =&gt; false, result: null")]
/// public bool TryParse(string s, out Person result)
/// </code></item>
/// </list></examples>
[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; }
}
/// <summary>
/// Indicates that marked element should be localized or not.
/// </summary>
/// <example><code>
/// [LocalizationRequiredAttribute(true)]
/// class Foo {
/// string str = "my string"; // Warning: Localizable string
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.All)]
internal sealed class LocalizationRequiredAttribute : Attribute
{
public LocalizationRequiredAttribute() : this(true) { }
public LocalizationRequiredAttribute(bool required)
{
Required = required;
}
public bool Required { get; private set; }
}
/// <summary>
/// Indicates that the value of the marked type (or its derivatives)
/// cannot be compared using '==' or '!=' operators and <c>Equals()</c>
/// should be used instead. However, using '==' or '!=' for comparison
/// with <c>null</c> is always permitted.
/// </summary>
/// <example><code>
/// [CannotApplyEqualityOperator]
/// class NoEquality { }
///
/// class UsesNoEquality {
/// void Test() {
/// var ca1 = new NoEquality();
/// var ca2 = new NoEquality();
/// if (ca1 != null) { // OK
/// bool condition = ca1 == ca2; // Warning
/// }
/// }
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { }
/// <summary>
/// When applied to a target attribute, specifies a requirement for any type marked
@ -108,6 +307,12 @@ namespace winPEAS.TaskScheduler
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;
@ -126,6 +331,12 @@ namespace winPEAS.TaskScheduler
[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) { }
@ -180,6 +391,88 @@ namespace winPEAS.TaskScheduler
internal sealed class PublicAPIAttribute : Attribute
{
public PublicAPIAttribute() { }
public PublicAPIAttribute([NotNull] string comment)
{
Comment = comment;
}
[CanBeNull] public string Comment { get; private set; }
}
/// <summary>
/// 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.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class InstantHandleAttribute : Attribute { }
/// <summary>
/// Indicates that a method does not make any observable state changes.
/// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>.
/// </summary>
/// <example><code>
/// [Pure] int Multiply(int x, int y) => x * y;
///
/// void M() {
/// Multiply(123, 42); // Waring: Return value of pure method is not used
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Method)]
internal sealed class PureAttribute : Attribute { }
/// <summary>
/// Indicates that the return value of method invocation must be used.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
internal sealed class MustUseReturnValueAttribute : Attribute
{
public MustUseReturnValueAttribute() { }
public MustUseReturnValueAttribute([NotNull] string justification)
{
Justification = justification;
}
[CanBeNull] public string Justification { get; private set; }
}
/// <summary>
/// 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.
/// </summary>
/// <example><code>
/// class Foo {
/// [ProvidesContext] IBarService _barService = ...;
///
/// void ProcessNode(INode node) {
/// DoSomething(node, node.GetGlobalServices().Bar);
/// // ^ Warning: use value of '_barService' field
/// }
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method |
AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)]
internal sealed class ProvidesContextAttribute : Attribute { }
/// <summary>
/// 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 (~).
/// </summary>
[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; }
}
/// <summary>
@ -262,6 +555,278 @@ namespace winPEAS.TaskScheduler
[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; }
}
/// <summary>
/// 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
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.
/// </summary>
[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; }
}
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC area.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcAreaAttribute : Attribute
{
public AspMvcAreaAttribute() { }
public AspMvcAreaAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
[CanBeNull] public string AnonymousProperty { get; private set; }
}
/// <summary>
/// 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
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>.
/// </summary>
[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; }
}
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute
/// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcMasterAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute
/// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, Object)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcModelTypeAttribute : Attribute { }
/// <summary>
/// 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
/// <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcPartialViewAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcDisplayTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcEditorTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC template.
/// Use this attribute for custom wrappers similar to
/// <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcTemplateAttribute : Attribute { }
/// <summary>
/// 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
/// <c>System.Web.Mvc.Controller.View(Object)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcViewAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC view component name.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcViewComponentAttribute : Attribute { }
/// <summary>
/// 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.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcViewComponentViewAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. When applied to a parameter of an attribute,
/// indicates that this parameter is an MVC action name.
/// </summary>
/// <example><code>
/// [ActionName("Foo")]
/// public ActionResult Login(string returnUrl) {
/// ViewBag.ReturnUrl = Url.Action("Foo"); // OK
/// return RedirectToAction("Bar"); // Error: Cannot resolve action
/// }
/// </code></example>
[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; }
}
/// <summary>
/// Razor attribute. Indicates that a parameter or a method is a Razor section.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class RazorSectionAttribute : Attribute { }
/// <summary>
/// Indicates how method, constructor invocation or property access
/// over collection type affects content of the collection.
/// </summary>
[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
{
/// <summary>Method does not use or modify content of the collection.</summary>
None = 0,
/// <summary>Method only reads content of the collection but does not modify it.</summary>
Read = 1,
/// <summary>Method can change content of the collection but does not add new elements.</summary>
ModifyExistingContent = 2,
/// <summary>Method can add new elements to the collection.</summary>
UpdatedContent = ModifyExistingContent | 4
}
/// <summary>
/// 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
@ -276,5 +841,199 @@ namespace winPEAS.TaskScheduler
/// the attribute is the assertion type.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AssertionConditionAttribute : Attribute { }
internal sealed class AssertionConditionAttribute : Attribute
{
public AssertionConditionAttribute(AssertionConditionType conditionType)
{
ConditionType = conditionType;
}
public AssertionConditionType ConditionType { get; private set; }
}
/// <summary>
/// Specifies assertion type. If the assertion method argument satisfies the condition,
/// then the execution continues. Otherwise, execution is assumed to be halted.
/// </summary>
internal enum AssertionConditionType
{
/// <summary>Marked parameter should be evaluated to true.</summary>
IS_TRUE = 0,
/// <summary>Marked parameter should be evaluated to false.</summary>
IS_FALSE = 1,
/// <summary>Marked parameter should be evaluated to null value.</summary>
IS_NULL = 2,
/// <summary>Marked parameter should be evaluated to not null value.</summary>
IS_NOT_NULL = 3,
}
/// <summary>
/// Indicates that the marked method unconditionally terminates control flow execution.
/// For example, it could unconditionally throw exception.
/// </summary>
[Obsolete("Use [ContractAnnotation('=> halt')] instead")]
[AttributeUsage(AttributeTargets.Method)]
internal sealed class TerminatesProgramAttribute : Attribute { }
/// <summary>
/// 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.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
internal sealed class LinqTunnelAttribute : Attribute { }
/// <summary>
/// Indicates that IEnumerable, passed as parameter, is not enumerated.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class NoEnumerationAttribute : Attribute { }
/// <summary>
/// Indicates that parameter is regular expression pattern.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class RegexPatternAttribute : Attribute { }
/// <summary>
/// Prevents the Member Reordering feature from tossing members of the marked class.
/// </summary>
/// <remarks>
/// The attribute must be mentioned in your member reordering patterns
/// </remarks>
[AttributeUsage(
AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)]
internal sealed class NoReorderAttribute : Attribute { }
/// <summary>
/// XAML attribute. Indicates the type that has <c>ItemsSource</c> property and should be treated
/// as <c>ItemsControl</c>-derived type, to enable inner items <c>DataContext</c> type resolve.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
internal sealed class XamlItemsControlAttribute : Attribute { }
/// <summary>
/// XAML attribute. Indicates the property of some <c>BindingBase</c>-derived type, that
/// is used to bind some item of <c>ItemsControl</c>-derived type. This annotation will
/// enable the <c>DataContext</c> type resolve for XAML bindings for such properties.
/// </summary>
/// <remarks>
/// Property should have the tree ancestor of the <c>ItemsControl</c> type or
/// marked with the <see cref="XamlItemsControlAttribute"/> attribute.
/// </remarks>
[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 { }
}

View File

@ -2,10 +2,13 @@
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.Native;
using winPEAS.TaskScheduler.V2;
using winPEAS.TaskScheduler.TaskEditor.Native;
using winPEAS.TaskScheduler.V2Interop;
namespace winPEAS.TaskScheduler
{
@ -273,6 +276,21 @@ namespace winPEAS.TaskScheduler
}
}
/// <summary>
/// Gets the value of the item at the specified index.
/// </summary>
/// <param name="index">The index of the item being requested.</param>
/// <returns>The value of the name-value pair at the specified index.</returns>
[NotNull]
public string this[int index]
{
get
{
if (v2Coll != null)
return v2Coll[++index].Value;
return unboundDict[index].Value;
}
}
/// <summary>
/// Gets the value of the item with the specified name.
@ -351,6 +369,22 @@ namespace winPEAS.TaskScheduler
Add(new NameValuePair(name, value));
}
/// <summary>
/// Adds the elements of the specified collection to the end of <see cref="NamedValueCollection"/>.
/// </summary>
/// <param name="items">The collection of whose elements should be added to the end of <see cref="NamedValueCollection"/>.</param>
public void AddRange([ItemNotNull, NotNull] IEnumerable<NameValuePair> 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));
}
/// <summary>
/// Clears the entire collection of name-value pairs.
/// </summary>
@ -432,6 +466,29 @@ namespace winPEAS.TaskScheduler
return false;
}
/// <summary>
/// Removes a selected name-value pair from the collection.
/// </summary>
/// <param name="index">Index of the pair to remove.</param>
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));
}
/// <summary>
/// Gets the value associated with the specified name.

View File

@ -1,24 +0,0 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace winPEAS.TaskScheduler.Native
{
internal static partial class NativeMethods
{
const string ADVAPI32 = "advapi32.dll";
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), DllImport(ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
public partial class SafeTokenHandle
{
private const Int32 ERROR_NO_TOKEN = 0x000003F0;
private const Int32 ERROR_INSUFFICIENT_BUFFER = 122;
private static SafeTokenHandle currentProcessToken = null;
}
}
}

View File

@ -1,55 +0,0 @@
using System;
namespace winPEAS.TaskScheduler.Native
{
internal static class EnumUtil
{
public static void CheckIsEnum<T>(bool checkHasFlags = false)
{
if (!typeof(T).IsEnum)
throw new ArgumentException($"Type '{typeof(T).FullName}' is not an enum");
if (checkHasFlags && !IsFlags<T>())
throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute");
}
public static bool IsFlags<T>() => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute));
public static bool IsFlagSet<T>(this T flags, T flag) where T : struct, IConvertible
{
CheckIsEnum<T>(true);
var flagValue = Convert.ToInt64(flag);
return (Convert.ToInt64(flags) & flagValue) == flagValue;
}
public static bool IsValidFlagValue<T>(this T flags) where T : struct, IConvertible
{
CheckIsEnum<T>(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<T>(ref T flags, T flag, bool set = true) where T : struct, IConvertible
{
CheckIsEnum<T>(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<T>(this T flags, T flag, bool set = true) where T : struct, IConvertible
{
var ret = flags;
SetFlags<T>(ref ret, flag, set);
return ret;
}
}
}

View File

@ -1,54 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
// ReSharper disable once CheckNamespace
namespace winPEAS.TaskScheduler.Native
{
internal class ComEnumerator<T, TIn> : IEnumerator<T> where T : class where TIn : class
{
protected readonly Func<TIn, T> converter;
protected IEnumerator<TIn> iEnum;
public ComEnumerator(Func<int> getCount, Func<int, TIn> indexer, Func<TIn, T> converter)
{
IEnumerator<TIn> Enumerate()
{
for (var x = 1; x <= getCount(); x++)
yield return indexer(x);
}
this.converter = converter;
iEnum = Enumerate();
}
public ComEnumerator(Func<int> getCount, Func<object, TIn> indexer, Func<TIn, T> converter)
{
IEnumerator<TIn> 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();
}
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace winPEAS.TaskScheduler.Native
{
internal static partial class NativeMethods
{
const string KERNEL32 = "Kernel32.dll";
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(KERNEL32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
public partial class SafeTokenHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
internal SafeTokenHandle(IntPtr handle, bool own = true) : base(own)
{
SetHandle(handle);
}
protected override bool ReleaseHandle() => CloseHandle(handle);
}
}
}

View File

@ -1,8 +0,0 @@
namespace winPEAS.TaskScheduler.Native
{
internal static partial class NativeMethods
{
}
}

View File

@ -1,8 +1,12 @@
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
{
@ -44,6 +48,16 @@ namespace winPEAS.TaskScheduler
min = minComp;
}
/// <summary>
/// Gets a message that describes the current exception.
/// </summary>
public override string Message => myMessage;
/// <summary>
/// Gets the minimum supported TaskScheduler version required for this method or property.
/// </summary>
public TaskCompatibility MinimumSupportedVersion => min;
internal abstract string LibName { get; }
/// <summary>
@ -93,6 +107,7 @@ namespace winPEAS.TaskScheduler
/// </summary>
/// <param name="serializationInfo">The serialization information.</param>
/// <param name="streamingContext">The streaming context.</param>
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)";
@ -109,6 +124,7 @@ namespace winPEAS.TaskScheduler
/// </summary>
/// <param name="serializationInfo">The serialization information.</param>
/// <param name="streamingContext">The streaming context.</param>
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})";
}

View File

@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{
@ -55,6 +59,18 @@ namespace winPEAS.TaskScheduler
return false;
}
/// <summary>Invokes a named method on a created instance of a type with parameters.</summary>
/// <typeparam name="T">The expected type of the method's return value.</typeparam>
/// <param name="type">The type to be instantiated and then used to invoke the method. This method assumes the type has a default public constructor.</param>
/// <param name="methodName">Name of the method.</param>
/// <param name="args">The arguments to provide to the method invocation.</param>
/// <returns>The value returned from the method.</returns>
public static T InvokeMethod<T>(Type type, string methodName, params object[] args)
{
object o = Activator.CreateInstance(type);
return InvokeMethod<T>(o, methodName, args);
}
/// <summary>Invokes a named method on a created instance of a type with parameters.</summary>
/// <typeparam name="T">The expected type of the method's return value.</typeparam>
/// <param name="type">The type to be instantiated and then used to invoke the method.</param>

View File

@ -1,47 +0,0 @@
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Some string values of <see cref="TaskDefinition"/> properties can be set to retrieve their value from existing DLLs as a resource. This class facilitates creating those reference strings.
/// </summary>
[PublicAPI]
public class ResourceReferenceValue
{
/// <summary>
/// Initializes a new instance of the <see cref="ResourceReferenceValue"/> class.
/// </summary>
/// <param name="dllPath">The DLL path.</param>
/// <param name="resourceId">The resource identifier.</param>
public ResourceReferenceValue([NotNull] string dllPath, int resourceId)
{
ResourceFilePath = dllPath;
ResourceIdentifier = resourceId;
}
/// <summary>
/// Gets or sets the resource file path. This can be a relative path, full path or lookup path (e.g. %SystemRoot%\System32\ResourceName.dll).
/// </summary>
/// <value>
/// The resource file path.
/// </value>
public string ResourceFilePath { get; set; }
/// <summary>
/// Gets or sets the resource identifier.
/// </summary>
/// <value>The resource identifier.</value>
public int ResourceIdentifier { get; set; }
/// <summary>
/// Performs an implicit conversion from <see cref="ResourceReferenceValue" /> to <see cref="System.String" />.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator string(ResourceReferenceValue value) => value.ToString();
/// <summary>
/// Returns a <see cref="System.String" /> in the format required by the Task Scheduler to reference a string in a DLL.
/// </summary>
/// <returns>A formatted <see cref="System.String" /> in the format $(@ [Dll], [ResourceID]).</returns>
public override string ToString() => $"$(@ {ResourceFilePath}, {ResourceIdentifier})";
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,8 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using winPEAS.TaskScheduler.Native;
using winPEAS.TaskScheduler.V1;
using winPEAS.TaskScheduler.V2;
using winPEAS.TaskScheduler.TaskEditor.Native;
namespace winPEAS.TaskScheduler
{
@ -15,11 +14,11 @@ namespace winPEAS.TaskScheduler
public sealed class RunningTaskCollection : IReadOnlyList<RunningTask>, IDisposable
{
private readonly TaskService svc;
private readonly IRunningTaskCollection v2Coll;
private readonly V2Interop.IRunningTaskCollection v2Coll;
internal RunningTaskCollection([NotNull] TaskService svc) => this.svc = svc;
internal RunningTaskCollection([NotNull] TaskService svc, [NotNull] IRunningTaskCollection iTaskColl)
internal RunningTaskCollection([NotNull] TaskService svc, [NotNull] V2Interop.IRunningTaskCollection iTaskColl)
{
this.svc = svc;
v2Coll = iTaskColl;
@ -74,9 +73,9 @@ namespace winPEAS.TaskScheduler
public IEnumerator<RunningTask> GetEnumerator()
{
if (v2Coll != null)
return new ComEnumerator<RunningTask, IRunningTask>(() => v2Coll.Count, (object o) => v2Coll[o], o =>
return new ComEnumerator<RunningTask, V2Interop.IRunningTask>(() => v2Coll.Count, (object o) => v2Coll[o], o =>
{
IRegisteredTask task = null;
V2Interop.IRegisteredTask task = null;
try { task = TaskService.GetTask(svc.v2TaskService, o.Path); } catch { }
return task == null ? null : new RunningTask(svc, task, o);
});
@ -152,9 +151,9 @@ namespace winPEAS.TaskScheduler
{
private readonly TaskFolder fld;
private readonly TaskService svc;
private readonly IRegisteredTaskCollection v2Coll;
private readonly V2Interop.IRegisteredTaskCollection v2Coll;
private Regex filter;
private ITaskScheduler v1TS;
private V1Interop.ITaskScheduler v1TS;
internal TaskCollection([NotNull] TaskService svc, Regex filter = null)
{
@ -163,7 +162,7 @@ namespace winPEAS.TaskScheduler
v1TS = svc.v1TaskScheduler;
}
internal TaskCollection([NotNull] TaskFolder folder, [NotNull] IRegisteredTaskCollection iTaskColl, Regex filter = null)
internal TaskCollection([NotNull] TaskFolder folder, [NotNull] V2Interop.IRegisteredTaskCollection iTaskColl, Regex filter = null)
{
svc = folder.TaskService;
Filter = filter;
@ -228,6 +227,24 @@ namespace winPEAS.TaskScheduler
}
}
/// <summary>Gets the named registered task from the collection.</summary>
/// <param name="name">The name of the registered task to be retrieved.</param>
/// <returns>A <see cref="Task"/> instance that contains the requested context.</returns>
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));
}
}
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
@ -236,6 +253,22 @@ namespace winPEAS.TaskScheduler
Marshal.ReleaseComObject(v2Coll);
}
/// <summary>Determines whether the specified task exists.</summary>
/// <param name="taskName">The name of the task.</param>
/// <returns>true if task exists; otherwise, false.</returns>
public bool Exists([NotNull] string taskName)
{
try
{
if (v2Coll != null)
return v2Coll[taskName] != null;
return svc.GetTask(taskName) != null;
}
catch { }
return false;
}
/// <summary>Gets the collection enumerator for the register task collection.</summary>
/// <returns>An <see cref="System.Collections.IEnumerator"/> for this collection.</returns>
public IEnumerator<Task> GetEnumerator()
@ -255,9 +288,9 @@ namespace winPEAS.TaskScheduler
{
private readonly Regex filter;
private readonly TaskService svc;
private readonly IEnumWorkItems wienum;
private readonly V1Interop.IEnumWorkItems wienum;
private string curItem;
private ITaskScheduler ts;
private V1Interop.ITaskScheduler ts;
/// <summary>Internal constructor</summary>
/// <param name="svc">TaskService instance</param>
@ -289,7 +322,7 @@ namespace winPEAS.TaskScheduler
}
}
internal ITask ICurrent => TaskService.GetTask(ts, curItem);
internal V1Interop.ITask ICurrent => TaskService.GetTask(ts, curItem);
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
@ -313,7 +346,7 @@ namespace winPEAS.TaskScheduler
wienum?.Next(1, out names, out uFetched);
if (uFetched != 1)
break;
using (var name = new CoTaskMemString(Marshal.ReadIntPtr(names)))
using (var name = new V1Interop.CoTaskMemString(Marshal.ReadIntPtr(names)))
curItem = name.ToString();
if (curItem != null && curItem.EndsWith(".job", StringComparison.InvariantCultureIgnoreCase))
curItem = curItem.Remove(curItem.Length - 4);
@ -328,7 +361,7 @@ namespace winPEAS.TaskScheduler
continue;
}
ITask itask = null;
V1Interop.ITask itask = null;
try { itask = ICurrent; valid = true; }
catch { valid = false; }
finally { Marshal.ReleaseComObject(itask); }
@ -345,11 +378,11 @@ namespace winPEAS.TaskScheduler
}
}
private class V2TaskEnumerator : ComEnumerator<Task, IRegisteredTask>
private class V2TaskEnumerator : ComEnumerator<Task, V2Interop.IRegisteredTask>
{
private readonly Regex filter;
internal V2TaskEnumerator(TaskFolder folder, IRegisteredTaskCollection iTaskColl, Regex filter = null) :
internal V2TaskEnumerator(TaskFolder folder, V2Interop.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()

View File

@ -0,0 +1,366 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler.TaskEditor.Native
{
internal static partial class NativeMethods
{
const string ADVAPI32 = "advapi32.dll";
[Flags]
public enum AccessTypes : uint
{
TokenAssignPrimary = 0x0001,
TokenDuplicate = 0x0002,
TokenImpersonate = 0x0004,
TokenQuery = 0x0008,
TokenQuerySource = 0x0010,
TokenAdjustPrivileges = 0x0020,
TokenAdjustGroups = 0x0040,
TokenAdjustDefault = 0x0080,
TokenAdjustSessionID = 0x0100,
TokenAllAccessP = 0x000F00FF,
TokenAllAccess = 0x000F01FF,
TokenRead = 0x00020008,
TokenWrite = 0x000200E0,
TokenExecute = 0x00020000,
Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,
StandardRightsRequired = 0x000F0000,
StandardRightsRead = 0x00020000,
StandardRightsWrite = 0x00020000,
StandardRightsExecute = 0x00020000,
StandardRightsAll = 0x001F0000,
SpecificRightsAll = 0x0000FFFF,
AccessSystemSecurity = 0x01000000,
MaximumAllowed = 0x02000000,
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
[Flags]
public enum PrivilegeAttributes : uint
{
Disabled = 0x00000000,
EnabledByDefault = 0x00000001,
Enabled = 0x00000002,
UsedForAccess = 0x80000000,
}
public enum SECURITY_IMPERSONATION_LEVEL
{
Anonymous,
Identification,
Impersonation,
Delegation
}
public enum TOKEN_ELEVATION_TYPE
{
Default = 1,
Full,
Limited
}
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
[Serializable]
public enum TokenType
{
TokenImpersonation = 2,
TokenPrimary = 1
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool AdjustTokenPrivileges([In] SafeTokenHandle TokenHandle, [In] bool DisableAllPrivileges, [In] ref TOKEN_PRIVILEGES NewState, [In] uint BufferLength, [In, Out] ref TOKEN_PRIVILEGES PreviousState, [In, Out] ref uint ReturnLength);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool AdjustTokenPrivileges([In] SafeTokenHandle TokenHandle, [In] bool DisableAllPrivileges, [In] ref TOKEN_PRIVILEGES NewState, [In] uint BufferLength, [In] IntPtr PreviousState, [In] IntPtr ReturnLength);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool ConvertStringSidToSid([In, MarshalAs(UnmanagedType.LPTStr)] string pStringSid, ref IntPtr sid);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool DuplicateToken(SafeTokenHandle ExistingTokenHandle, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, out SafeTokenHandle DuplicateTokenHandle);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), DllImport(ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateTokenEx([In] SafeTokenHandle ExistingTokenHandle, [In] AccessTypes DesiredAccess, [In] IntPtr TokenAttributes, [In] SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, [In] TokenType TokenType, [In, Out] ref SafeTokenHandle DuplicateTokenHandle);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetSidSubAuthority(IntPtr pSid, UInt32 nSubAuthority);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetTokenInformation(SafeTokenHandle hToken, TOKEN_INFORMATION_CLASS tokenInfoClass, IntPtr pTokenInfo, Int32 tokenInfoLength, out Int32 returnLength);
[DllImport(ADVAPI32, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport(ADVAPI32, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool LookupAccountSid(string systemName, byte[] accountSid, StringBuilder accountName, ref int nameLength, StringBuilder domainName, ref int domainLength, out int accountType);
[DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool LookupAccountSid([In, MarshalAs(UnmanagedType.LPTStr)] string systemName, IntPtr sid, StringBuilder name, ref int cchName, StringBuilder referencedDomainName, ref int cchReferencedDomainName, out int use);
[DllImport(ADVAPI32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeValue(string systemName, string name, out LUID luid);
[DllImport(ADVAPI32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenProcessToken(IntPtr ProcessHandle, AccessTypes DesiredAccess, out SafeTokenHandle TokenHandle);
[DllImport(ADVAPI32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenThreadToken(IntPtr ThreadHandle, AccessTypes DesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool OpenAsSelf, out SafeTokenHandle TokenHandle);
[DllImport(ADVAPI32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PrivilegeCheck(IntPtr ClientToken, ref PRIVILEGE_SET RequiredPrivileges, out int result);
[DllImport(ADVAPI32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RevertToSelf();
[DllImport(ADVAPI32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetThreadToken(IntPtr ThreadHandle, SafeTokenHandle TokenHandle);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LUID
{
public uint LowPart;
public int HighPart;
public static LUID FromName(string name, string systemName = null)
{
LUID val;
if (!NativeMethods.LookupPrivilegeValue(systemName, name, out val))
throw new System.ComponentModel.Win32Exception();
return val;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public PrivilegeAttributes Attributes;
public LUID_AND_ATTRIBUTES(LUID luid, PrivilegeAttributes attr)
{
Luid = luid;
Attributes = attr;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct PRIVILEGE_SET : IDisposable
{
public uint PrivilegeCount;
public uint Control;
public IntPtr Privilege;
public PRIVILEGE_SET(uint control, params LUID_AND_ATTRIBUTES[] privileges)
{
PrivilegeCount = (uint)privileges.Length;
Control = control;
Privilege = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LUID_AND_ATTRIBUTES)) * (int)PrivilegeCount);
for (int i = 0; i < PrivilegeCount; i++)
Marshal.StructureToPtr(privileges[i], (IntPtr)((int)Privilege + (Marshal.SizeOf(typeof(LUID_AND_ATTRIBUTES)) * i)), false);
}
public void Dispose()
{
Marshal.FreeHGlobal(Privilege);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_ELEVATION
{
public Int32 TokenIsElevated;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_MANDATORY_LABEL
{
public SID_AND_ATTRIBUTES Label;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TOKEN_PRIVILEGES
{
public uint PrivilegeCount;
public LUID_AND_ATTRIBUTES Privileges;
public TOKEN_PRIVILEGES(LUID luid, PrivilegeAttributes attribute)
{
PrivilegeCount = 1;
Privileges.Luid = luid;
Privileges.Attributes = attribute;
}
public static uint SizeInBytes => (uint)Marshal.SizeOf(typeof(TOKEN_PRIVILEGES));
}
public partial class SafeTokenHandle
{
private const Int32 ERROR_NO_TOKEN = 0x000003F0;
private const Int32 ERROR_INSUFFICIENT_BUFFER = 122;
private static SafeTokenHandle currentProcessToken = null;
public T GetInfo<T>(TOKEN_INFORMATION_CLASS type)
{
int cbSize = Marshal.SizeOf(typeof(T));
IntPtr pType = Marshal.AllocHGlobal(cbSize);
try
{
// Retrieve token information.
if (!NativeMethods.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));
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.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(NativeMethods.GetCurrentProcess(), desiredAccess);
return currentProcessToken;
}
}
public static SafeTokenHandle FromCurrentThread(AccessTypes desiredAccess = AccessTypes.TokenDuplicate, bool openAsSelf = true) => FromThread(NativeMethods.GetCurrentThread(), desiredAccess, openAsSelf);
public static SafeTokenHandle FromProcess(IntPtr hProcess, AccessTypes desiredAccess = AccessTypes.TokenDuplicate)
{
SafeTokenHandle val;
if (!NativeMethods.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 (!NativeMethods.OpenThreadToken(hThread, desiredAccess, openAsSelf, out val))
{
if (Marshal.GetLastWin32Error() == ERROR_NO_TOKEN)
{
SafeTokenHandle pval = FromCurrentProcess();
if (!NativeMethods.DuplicateTokenEx(pval, AccessTypes.TokenImpersonate | desiredAccess, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.Impersonation, TokenType.TokenImpersonation, ref val))
throw new System.ComponentModel.Win32Exception();
if (!NativeMethods.SetThreadToken(IntPtr.Zero, val))
throw new System.ComponentModel.Win32Exception();
}
else
throw new System.ComponentModel.Win32Exception();
}
return val;
}
}
}
}

View File

@ -0,0 +1,147 @@
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<T>(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;
}
/// <summary>
/// Converts an <see cref="IntPtr"/> that points to a C-style array into a CLI array.
/// </summary>
/// <typeparam name="TS">Type of native structure used by the C-style array.</typeparam>
/// <typeparam name="T">Output type for the CLI array. <typeparamref name="TS"/> must be able to convert to <typeparamref name="T"/>.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <returns>An array of type <typeparamref name="T"/> containing the converted elements of the native array.</returns>
public static T[] ToArray<TS, T>(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<TS>(tempPtr);
ret[i] = (T)Convert.ChangeType(val, typeof(T));
}
return ret;
}
/// <summary>
/// Converts an <see cref="IntPtr"/> that points to a C-style array into a CLI array.
/// </summary>
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <returns>An array of type <typeparamref name="T"/> containing the elements of the native array.</returns>
public static T[] ToArray<T>(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<T>(tempPtr);
}
return ret;
}
}
internal class ComEnumerator<T, TIn> : IEnumerator<T> where T : class where TIn : class
{
protected readonly Func<TIn, T> converter;
protected IEnumerator<TIn> iEnum;
public ComEnumerator(Func<int> getCount, Func<int, TIn> indexer, Func<TIn, T> converter)
{
IEnumerator<TIn> Enumerate()
{
for (var x = 1; x <= getCount(); x++)
yield return indexer(x);
}
this.converter = converter;
iEnum = Enumerate();
}
public ComEnumerator(Func<int> getCount, Func<object, TIn> indexer, Func<TIn, T> converter)
{
IEnumerator<TIn> 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();
}
}
}

View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler.TaskEditor.Native
{
internal static partial class NativeMethods
{
const string KERNEL32 = "Kernel32.dll";
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(KERNEL32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetCurrentThread();
/// <summary>
/// The GlobalLock function locks a global memory object and returns a pointer to the first byte of the object's memory block.
/// GlobalLock function increments the lock count by one.
/// Needed for the clipboard functions when getting the data from IDataObject
/// </summary>
/// <param name="hMem"></param>
/// <returns></returns>
[DllImport(KERNEL32, SetLastError = true)]
public static extern IntPtr GlobalLock(IntPtr hMem);
/// <summary>
/// The GlobalUnlock function decrements the lock count associated with a memory object.
/// </summary>
/// <param name="hMem"></param>
/// <returns></returns>
[DllImport(KERNEL32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GlobalUnlock(IntPtr hMem);
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string filename);
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr lib);
public partial class SafeTokenHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle() : base(true) { }
internal SafeTokenHandle(IntPtr handle, bool own = true) : base(own)
{
SetHandle(handle);
}
protected override bool ReleaseHandle() => CloseHandle(handle);
}
}
}

View File

@ -1,9 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler.Native
namespace winPEAS.TaskScheduler.TaskEditor.Native
{
internal static partial class NativeMethods
{

View File

@ -0,0 +1,191 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler.TaskEditor.Native
{
internal static partial class NativeMethods
{
const int MAX_PREFERRED_LENGTH = -1;
[Flags]
public enum ServerTypes : uint
{
Workstation = 0x00000001,
Server = 0x00000002,
SqlServer = 0x00000004,
DomainCtrl = 0x00000008,
BackupDomainCtrl = 0x00000010,
TimeSource = 0x00000020,
AppleFilingProtocol = 0x00000040,
Novell = 0x00000080,
DomainMember = 0x00000100,
PrintQueueServer = 0x00000200,
DialinServer = 0x00000400,
XenixServer = 0x00000800,
UnixServer = 0x00000800,
NT = 0x00001000,
WindowsForWorkgroups = 0x00002000,
MicrosoftFileAndPrintServer = 0x00004000,
NTServer = 0x00008000,
BrowserService = 0x00010000,
BackupBrowserService = 0x00020000,
MasterBrowserService = 0x00040000,
DomainMaster = 0x00080000,
OSF1Server = 0x00100000,
VMSServer = 0x00200000,
Windows = 0x00400000,
DFS = 0x00800000,
NTCluster = 0x01000000,
TerminalServer = 0x02000000,
VirtualNTCluster = 0x04000000,
DCE = 0x10000000,
AlternateTransport = 0x20000000,
LocalListOnly = 0x40000000,
PrimaryDomain = 0x80000000,
All = 0xFFFFFFFF
};
public enum ServerPlatform
{
DOS = 300,
OS2 = 400,
NT = 500,
OSF = 600,
VMS = 700
}
[DllImport("Netapi32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int NetServerGetInfo(string serverName, int level, out IntPtr pSERVER_INFO_XXX);
[DllImport("Netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
private static extern int NetServerEnum(
[MarshalAs(UnmanagedType.LPWStr)] string servernane, // must be null
int level,
out IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
ServerTypes servertype,
[MarshalAs(UnmanagedType.LPWStr)] string domain, // null for login domain
IntPtr resume_handle // Must be IntPtr.Zero
);
[DllImport("Netapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
private static extern int NetApiBufferFree(IntPtr pBuf);
[StructLayout(LayoutKind.Sequential)]
public struct SERVER_INFO_100
{
public ServerPlatform PlatformId;
[MarshalAs(System.Runtime.InteropServices.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_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;
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<string> GetNetworkComputerNames(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null) =>
Array.ConvertAll(NetServerEnum<SERVER_INFO_100>(serverTypes, domain), si => si.Name);
public static IEnumerable<NetworkComputerInfo> GetNetworkComputerInfo(ServerTypes serverTypes = ServerTypes.Workstation | ServerTypes.Server, string domain = null) =>
NetServerEnum<NetworkComputerInfo>(serverTypes, domain, 101);
public static T[] NetServerEnum<T>(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;
int ret = NetServerEnum(null, level, out bufptr, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, serverTypes, domain, resumeHandle);
if (ret == 0)
return InteropUtil.ToArray<T>(bufptr, entriesRead);
throw new System.ComponentModel.Win32Exception(ret);
}
finally
{
NetApiBufferFree(bufptr);
}
}
public static T NetServerGetInfo<T>(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 = NetServerGetInfo(serverName, level, out ptr);
if (ret != 0)
throw new System.ComponentModel.Win32Exception(ret);
return (T)Marshal.PtrToStructure(ptr, typeof(T));
}
finally
{
if (ptr != IntPtr.Zero)
NetApiBufferFree(ptr);
}
}
}
}

View File

@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler.Native
namespace winPEAS.TaskScheduler.TaskEditor.Native
{
internal static partial class NativeMethods
{
@ -30,6 +34,18 @@ namespace winPEAS.TaskScheduler.Native
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 static implicit operator DateTime(SYSTEMTIME st)
{
if (st.Year == 0 || st == MinValue)
@ -47,6 +63,11 @@ namespace winPEAS.TaskScheduler.Native
public static readonly SYSTEMTIME MinValue, MaxValue;
static SYSTEMTIME()
{
MinValue = new SYSTEMTIME(1601, 1, 1);
MaxValue = new SYSTEMTIME(30827, 12, 31, 23, 59, 59, 999);
}
public override bool Equals(object obj)
{

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{
@ -396,6 +396,22 @@ namespace winPEAS.TaskScheduler
/// </summary>
public EventRecord EventRecord { get; internal set; }
/// <summary>
/// Gets the <see cref="StandardTaskEventId"/> from the <see cref="EventId"/>.
/// </summary>
/// <value>
/// The <see cref="StandardTaskEventId"/>. If not found, returns <see cref="StandardTaskEventId.Unknown"/>.
/// </value>
public StandardTaskEventId StandardEventId
{
get
{
if (Enum.IsDefined(typeof(StandardTaskEventId), EventId))
return (StandardTaskEventId)EventId;
return StandardTaskEventId.Unknown;
}
}
/// <summary>
/// Gets the level. This value is <c>null</c> for V1 events.
/// </summary>
@ -441,6 +457,14 @@ namespace winPEAS.TaskScheduler
/// </summary>
public byte? Version { get; internal set; }
/// <summary>
/// Gets the data value from the task specific event data item list.
/// </summary>
/// <param name="name">The name of the data element.</param>
/// <returns>Contents of the requested data element if found. <c>null</c> if no value found.</returns>
[Obsolete("Use the DataVales property instead.")]
public string GetDataValue(string name) => DataValues?[name];
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
@ -479,6 +503,29 @@ namespace winPEAS.TaskScheduler
{
rec = eventRec;
}
/// <summary>
/// Gets the <see cref="System.String"/> value of the specified property name.
/// </summary>
/// <value>
/// The value.
/// </value>
/// <param name="propertyName">Name of the property.</param>
/// <returns>Value of the specified property name. <c>null</c> if property does not exist.</returns>
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;
}
}
}
}
@ -542,6 +589,26 @@ namespace winPEAS.TaskScheduler
{
log.Seek(System.IO.SeekOrigin.Begin, 0L);
}
/// <summary>
/// Seeks the specified bookmark.
/// </summary>
/// <param name="bookmark">The bookmark.</param>
/// <param name="offset">The offset.</param>
public void Seek(EventBookmark bookmark, long offset = 0L)
{
log.Seek(bookmark, offset);
}
/// <summary>
/// Seeks the specified origin.
/// </summary>
/// <param name="origin">The origin.</param>
/// <param name="offset">The offset.</param>
public void Seek(System.IO.SeekOrigin origin, long offset)
{
log.Seek(origin, offset);
}
}
/// <summary>
@ -577,6 +644,16 @@ namespace winPEAS.TaskScheduler
private const string TSEventLogPath = "Microsoft-Windows-TaskScheduler/Operational";
private static readonly bool IsVistaOrLater = Environment.OSVersion.Version.Major >= 6;
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventLog"/> class.
/// </summary>
/// <param name="taskPath">The task path. This can be retrieved using the <see cref="Task.Path"/> property.</param>
/// <exception cref="NotSupportedException">Thrown when instantiated on an OS prior to Windows Vista.</exception>
public TaskEventLog([CanBeNull] string taskPath) : this(".", taskPath)
{
Initialize(".", BuildQuery(taskPath), true);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventLog" /> class.
/// </summary>
@ -591,6 +668,36 @@ namespace winPEAS.TaskScheduler
Initialize(machineName, BuildQuery(taskPath), true, domain, user, password);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventLog" /> class that looks at all task events from a specified time.
/// </summary>
/// <param name="startTime">The start time.</param>
/// <param name="taskName">Name of the task.</param>
/// <param name="machineName">Name of the machine (optional).</param>
/// <param name="domain">The domain.</param>
/// <param name="user">The user.</param>
/// <param name="password">The password.</param>
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);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventLog"/> class.
/// </summary>
/// <param name="taskName">Name of the task.</param>
/// <param name="eventIDs">The event ids.</param>
/// <param name="startTime">The start time.</param>
/// <param name="machineName">Name of the machine (optional).</param>
/// <param name="domain">The domain.</param>
/// <param name="user">The user.</param>
/// <param name="password">The password.</param>
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);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventLog" /> class.
/// </summary>
@ -670,6 +777,63 @@ namespace winPEAS.TaskScheduler
Query.Session = new EventLogSession(machineName, domain, user, spwd, SessionAuthentication.Default);
}
/// <summary>
/// Gets the total number of events for this task.
/// </summary>
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;
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="TaskEventLog" /> is enabled.
/// </summary>
/// <value>
/// <c>true</c> if enabled; otherwise, <c>false</c>.
/// </value>
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();
}
}
}
}
/// <summary>
/// Gets or sets a value indicating whether to enumerate in reverse when calling the default enumerator (typically with foreach statement).
/// </summary>

View File

@ -1,698 +0,0 @@
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics.Eventing.Reader;
using System.IO;
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Information about the task event.
/// </summary>
[PublicAPI]
public class TaskEventArgs : EventArgs
{
private readonly TaskService taskService;
internal TaskEventArgs([NotNull] TaskEvent evt, TaskService ts = null)
{
TaskEvent = evt;
TaskPath = evt.TaskPath;
taskService = ts;
}
/// <summary>
/// Gets the <see cref="TaskEvent"/>.
/// </summary>
/// <value>
/// The TaskEvent.
/// </value>
[NotNull]
public TaskEvent TaskEvent { get; }
/// <summary>
/// Gets the task path.
/// </summary>
/// <value>
/// The task path.
/// </value>
public string TaskPath { get; }
}
/// <summary>
/// Watches system events related to tasks and issues a <see cref="TaskEventWatcher.EventRecorded"/> event when the filtered conditions are met.
/// <note>Only available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and later.</note>
/// </summary>
/// <remarks>Sometimes, a developer will need to know about events as they occur. In this case, they can use the TaskEventWatcher component that enables the developer to watch a task, a folder, or the entire system for filtered events.</remarks>
/// <example>
/// <para>Below is information on how to watch a folder for all task events. For a complete example, look at this sample project: TestTaskWatcher.zip</para>
/// <code lang="cs"><![CDATA[
/// private TaskEventWatcher watcher;
///
/// // Create and configure a new task watcher for the task folder
/// private void SetupWatcher(TaskFolder tf)
/// {
/// if (tf != null)
/// {
/// // Set up a watch over the supplied task folder.
/// watcher = new TaskEventWatcher(tf);
///
/// // Assign a SynchronizingObject to a local UI class to synchronize the events in this thread.
/// watcher.SynchronizingObject = this;
///
/// // Only watch for tasks that start with my company name
/// watcher.Filter.TaskName = "MyCo*";
///
/// // Only watch for task events that are informational
/// watcher.Filter.EventLevels = new int[]
/// { 0 /* StandardEventLevel.LogAlways */, (int)StandardEventLevel.Informational };
///
/// // Assign an event handler for when events are recorded
/// watcher.EventRecorded += Watcher_EventRecorded;
///
/// // Start watching the folder by enabling the watcher
/// watcher.Enabled = true;
/// }
/// }
///
/// // Cleanup and release the task watcher
/// private void TearDownWatcher()
/// {
/// if (watcher != null)
/// {
/// // Unhook the event
/// watcher.EventRecorded -= Watcher_EventRecorded;
/// // Stop watching for events
/// watcher.Enabled = false;
/// // Initiate garbage collection for the watcher
/// watcher = null;
/// }
/// }
///
/// // Update ListView instance when task events occur
/// private void Watcher_EventRecorded(object sender, TaskEventArgs e)
/// {
/// int idx = IndexOfTask(e.TaskName);
///
/// // If event is for a task we already have in the list...
/// if (idx != -1)
/// {
/// // If event indicates that task has been deleted, remove it from the list
/// if (e.TaskEvent.StandardEventId == StandardTaskEventId.TaskDeleted)
/// {
/// listView1.Items.RemoveAt(idx);
/// }
///
/// // If event is anything else, it most likely represents a change,
/// // so update the item using information supplied through the
/// // TaskEventArgs instance.
/// else
/// {
/// var lvi = listView1.Items[idx];
/// lvi.Subitems[0].Text = e.TaskName;
/// lvi.Subitems[1].Text = e.Task.State.ToString();
/// lvi.Subitems[2].Text = GetNextRunTimeString(e.Task);
/// }
/// }
///
/// // If event is for a task we don't have in our list, add it
/// else
/// {
/// var lvi = new ListViewItem(new string[] { e.TaskName,
/// e.Task.State.ToString(), GetNextRunTimeString(e.Task) });
/// listView1.Items.Add(lvi);
/// listView1.Sort();
/// }
/// }
///
/// // Get the next run time for a task
/// private string GetNextRunTimeString(Task t)
/// {
/// if (t.State == TaskState.Disabled || t.NextRunTime < DateTime.Now)
/// return string.Empty;
/// return t.NextRunTime.ToString("G");
/// }
/// ]]></code></example>
[DefaultEvent(nameof(EventRecorded)), DefaultProperty(nameof(Folder))]
#if DESIGNER
[Designer(typeof(Design.TaskEventWatcherDesigner))]
#endif
[ToolboxItem(true), Serializable]
[PublicAPI]
public class TaskEventWatcher : Component, ISupportInitialize
{
private const string root = "\\";
private const string star = "*";
private static readonly TimeSpan MaxV1EventLapse = TimeSpan.FromSeconds(1);
private bool disposed;
private bool enabled;
private string folder = root;
private bool includeSubfolders;
private bool initializing;
private StandardTaskEventId lastId = 0;
private DateTime lastIdTime = DateTime.MinValue;
private TaskService ts;
private FileSystemWatcher v1Watcher;
private EventLogWatcher watcher;
private ISynchronizeInvoke synchronizingObject;
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher"/> class. If other
/// properties are not set, this will watch for all events for all tasks on the local machine.
/// </summary>
public TaskEventWatcher() : this(TaskService.Instance)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher" /> class watching only
/// those events for the task with the provided path on the local machine.
/// </summary>
/// <param name="taskPath">The full path (folders and name) of the task to watch.</param>
/// <param name="taskService">The task service.</param>
/// <exception cref="System.ArgumentException">$Invalid task name: {taskPath}</exception>
public TaskEventWatcher(string taskPath, TaskService taskService = null) : this(taskService ?? TaskService.Instance)
{
InitTask(taskPath);
}
private TaskEventWatcher(TaskService ts)
{
TaskService = ts;
Filter = new EventFilter(this);
}
/// <summary>
/// Occurs when a task or the task engine records an event.
/// </summary>
[Category("Action"), Description("Event recorded by a task or the task engine.")]
public event EventHandler<TaskEventArgs> EventRecorded;
/// <summary>
/// Gets or sets a value indicating whether the component is enabled.
/// </summary>
/// <value>
/// <c>true</c> if enabled; otherwise, <c>false</c>.
/// </value>
[DefaultValue(false), Category("Behavior"), Description("Indicates whether the component is enabled.")]
public bool Enabled
{
get { return enabled; }
set
{
if (enabled != value)
{
System.Diagnostics.Debug.WriteLine($"TaskEventWather: Set {nameof(Enabled)} = {value}");
enabled = value;
if (!IsSuspended())
{
if (enabled)
StartRaisingEvents();
else
StopRaisingEvents();
}
}
}
}
/// <summary>
/// Gets the filter for this <see cref="TaskEventWatcher"/>.
/// </summary>
/// <value>
/// The filter.
/// </value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Category("Behavior"), Description("Indicates the filter for the watcher.")]
public EventFilter Filter { get; }
/// <summary>
/// Gets or sets the folder to watch.
/// </summary>
/// <value>
/// The folder path to watch. This value should include the leading "\" to indicate the root folder.
/// </value>
/// <exception cref="System.ArgumentException">Thrown if the folder specified does not exist or contains invalid characters.</exception>
[DefaultValue(root), Category("Behavior"), Description("Indicates the folder to watch.")]
public string Folder
{
get { return folder; }
set
{
if (string.IsNullOrEmpty(value))
value = root;
if (!value.EndsWith("\\"))
value += "\\";
if (string.Compare(folder, value, StringComparison.OrdinalIgnoreCase) == 0) return;
if ((DesignMode && (value.IndexOfAny(new[] { '*', '?' }) != -1 || value.IndexOfAny(Path.GetInvalidPathChars()) != -1)) || (TaskService.GetFolder(value == root ? value : value.TrimEnd('\\')) == null))
throw new ArgumentException($"Invalid folder name: {value}");
folder = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether to include events from subfolders when the
/// <see cref="Folder"/> property is set. If the <see cref="TaskEventWatcher.EventFilter.TaskName"/> property is set,
/// this property is ignored.
/// </summary>
/// <value><c>true</c> if include events from subfolders; otherwise, <c>false</c>.</value>
[DefaultValue(false), Category("Behavior"), Description("Indicates whether to include events from subfolders.")]
public bool IncludeSubfolders
{
get { return includeSubfolders; }
set
{
if (includeSubfolders == value) return;
includeSubfolders = value;
Restart();
}
}
/// <summary>
/// Gets or sets the synchronizing object.
/// </summary>
/// <value>
/// The synchronizing object.
/// </value>
[Browsable(false), DefaultValue(null)]
public ISynchronizeInvoke SynchronizingObject
{
get
{
if (synchronizingObject == null && DesignMode)
{
var so = ((IDesignerHost)GetService(typeof(IDesignerHost)))?.RootComponent as ISynchronizeInvoke;
if (so != null)
synchronizingObject = so;
}
return synchronizingObject;
}
set { synchronizingObject = value; }
}
/// <summary>
/// Gets or sets the name of the computer that is running the Task Scheduler service that the user is connected to.
/// </summary>
[Category("Connection"), Description("The name of the computer to connect to."), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string TargetServer
{
get { return TaskService.TargetServer; }
set
{
if (value == null || value.Trim() == string.Empty) value = null;
if (string.Compare(value, TaskService.TargetServer, StringComparison.OrdinalIgnoreCase) == 0) return;
TaskService.TargetServer = value;
Restart();
}
}
/// <summary>
/// Gets or sets the <see cref="TaskService"/> instance associated with this event watcher. Setting this value
/// will override any values set for <see cref="TargetServer"/>, <see cref="UserAccountDomain"/>,
/// <see cref="UserName"/>, and <see cref="UserPassword"/> and set them to those values in the supplied
/// <see cref="TaskService"/> instance.
/// </summary>
/// <value>The TaskService.</value>
[Category("Data"), Description("The TaskService for this event watcher.")]
public TaskService TaskService
{
get { return ts; }
set { ts = value; Restart(); }
}
/// <summary>
/// Gets or sets the user account domain to be used when connecting to the <see cref="TargetServer"/>.
/// </summary>
/// <value>The user account domain.</value>
[Category("Connection"), Description("The user account domain to be used when connecting."), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string UserAccountDomain
{
get { return TaskService.UserAccountDomain; }
set
{
if (value == null || value.Trim() == string.Empty) value = null;
if (string.Compare(value, TaskService.UserAccountDomain, StringComparison.OrdinalIgnoreCase) == 0) return;
TaskService.UserAccountDomain = value;
Restart();
}
}
/// <summary>
/// Gets or sets the user name to be used when connecting to the <see cref="TargetServer"/>.
/// </summary>
/// <value>The user name.</value>
[Category("Connection"), Description("The user name to be used when connecting."), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string UserName
{
get { return TaskService.UserName; }
set
{
if (value == null || value.Trim() == string.Empty) value = null;
if (string.Compare(value, TaskService.UserName, StringComparison.OrdinalIgnoreCase) == 0) return;
TaskService.UserName = value;
Restart();
}
}
/// <summary>
/// Gets or sets the user password to be used when connecting to the <see cref="TargetServer"/>.
/// </summary>
/// <value>The user password.</value>
[Category("Connection"), Description("The user password to be used when connecting."), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string UserPassword
{
get { return TaskService.UserPassword; }
set
{
if (value == null || value.Trim() == string.Empty) value = null;
if (string.Compare(value, TaskService.UserPassword, StringComparison.OrdinalIgnoreCase) == 0) return;
TaskService.UserPassword = value;
Restart();
}
}
/// <summary>
/// Gets a value indicating if watching is available.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
private bool IsHandleInvalid => IsV1 ? v1Watcher == null : watcher == null;
private static bool IsV1 => Environment.OSVersion.Version.Major < 6;
/// <summary>
/// Signals the object that initialization is starting.
/// </summary>
public void BeginInit()
{
System.Diagnostics.Debug.WriteLine($"TaskEventWather: {nameof(BeginInit)}");
initializing = true;
var localEnabled = enabled;
StopRaisingEvents();
enabled = localEnabled;
TaskService.BeginInit();
}
/// <summary>
/// Signals the object that initialization is complete.
/// </summary>
public void EndInit()
{
System.Diagnostics.Debug.WriteLine($"TaskEventWather: {nameof(EndInit)}");
initializing = false;
TaskService.EndInit();
if (enabled)
StartRaisingEvents();
}
/// <summary>
/// Releases the unmanaged resources used by the FileSystemWatcher and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
StopRaisingEvents();
TaskService = null;
}
else
{
StopListening();
}
}
finally
{
disposed = true;
base.Dispose(disposing);
}
}
/// <summary>
/// Fires the <see cref="EventRecorded"/> event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="TaskEventArgs" /> instance containing the event data.</param>
protected virtual void OnEventRecorded(object sender, TaskEventArgs e)
{
var h = EventRecorded;
if (h == null) return;
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
SynchronizingObject.BeginInvoke(h, new object[] { this, e });
else
h(sender, e);
}
private void InitTask(string taskPath)
{
Filter.TaskName = Path.GetFileNameWithoutExtension(taskPath);
Folder = Path.GetDirectoryName(taskPath);
}
private bool IsSuspended() => initializing || DesignMode;
private void ReleaseWatcher()
{
if (IsV1)
{
if (v1Watcher == null) return;
v1Watcher.EnableRaisingEvents = false;
v1Watcher.Changed -= Watcher_DirectoryChanged;
v1Watcher.Created -= Watcher_DirectoryChanged;
v1Watcher.Deleted -= Watcher_DirectoryChanged;
v1Watcher.Renamed -= Watcher_DirectoryChanged;
v1Watcher = null;
}
else
{
if (watcher == null) return;
watcher.Enabled = false;
watcher.EventRecordWritten -= Watcher_EventRecordWritten;
watcher = null;
}
}
private void Restart()
{
if (IsSuspended() || !enabled) return;
System.Diagnostics.Debug.WriteLine($"TaskEventWather: {nameof(Restart)}");
StopRaisingEvents();
StartRaisingEvents();
}
private void SetupWatcher()
{
ReleaseWatcher();
string taskPath = null;
if (Filter.Wildcard == null)
taskPath = Path.Combine(folder, Filter.TaskName);
if (IsV1)
{
var di = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.System));
string dir = di.Parent != null ? Path.Combine(di.Parent.FullName, "Tasks") : "Tasks";
v1Watcher = new FileSystemWatcher(dir, "*.job") { NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Attributes };
v1Watcher.Changed += Watcher_DirectoryChanged;
v1Watcher.Created += Watcher_DirectoryChanged;
v1Watcher.Deleted += Watcher_DirectoryChanged;
v1Watcher.Renamed += Watcher_DirectoryChanged;
}
else
{
var log = new TaskEventLog(taskPath, Filter.EventIds, Filter.EventLevels, DateTime.UtcNow, TargetServer, UserAccountDomain, UserName, UserPassword);
log.Query.ReverseDirection = false;
watcher = new EventLogWatcher(log.Query);
watcher.EventRecordWritten += Watcher_EventRecordWritten;
}
}
private void StartRaisingEvents()
{
if (disposed)
throw new ObjectDisposedException(GetType().Name);
if (IsSuspended()) return;
System.Diagnostics.Debug.WriteLine($"TaskEventWather: {nameof(StartRaisingEvents)}");
enabled = true;
SetupWatcher();
if (IsV1)
try { v1Watcher.EnableRaisingEvents = true; } catch { }
else
try { watcher.Enabled = true; } catch { }
}
private void StopListening()
{
enabled = false;
ReleaseWatcher();
}
private void StopRaisingEvents()
{
System.Diagnostics.Debug.WriteLine($"TaskEventWather: {nameof(StopRaisingEvents)}");
if (IsSuspended())
enabled = false;
else if (!IsHandleInvalid)
StopListening();
}
private void Watcher_DirectoryChanged(object sender, FileSystemEventArgs e)
{
StandardTaskEventId id = StandardTaskEventId.TaskUpdated;
if (e.ChangeType == WatcherChangeTypes.Deleted)
id = StandardTaskEventId.TaskDeleted;
else if (e.ChangeType == WatcherChangeTypes.Created)
id = StandardTaskEventId.JobRegistered;
if (lastId == id && DateTime.Now.Subtract(lastIdTime) <= MaxV1EventLapse) return;
OnEventRecorded(this, new TaskEventArgs(new TaskEvent(Path.Combine("\\", e.Name.Replace(".job", "")), id, DateTime.Now), TaskService));
lastId = id;
lastIdTime = DateTime.Now;
}
private void Watcher_EventRecordWritten(object sender, EventRecordWrittenEventArgs e)
{
try
{
var taskEvent = new TaskEvent(e.EventRecord);
System.Diagnostics.Debug.WriteLine("Task event: " + taskEvent.ToString());
// Get the task name and folder
if (string.IsNullOrEmpty(taskEvent.TaskPath)) return;
int cpos = taskEvent.TaskPath.LastIndexOf('\\');
string name = taskEvent.TaskPath.Substring(cpos + 1);
string fld = taskEvent.TaskPath.Substring(0, cpos + 1);
// Check folder and name filters
if (!string.IsNullOrEmpty(Filter.TaskName) && string.Compare(Filter.TaskName, taskEvent.TaskPath, StringComparison.OrdinalIgnoreCase) != 0)
{
if (Filter.Wildcard != null && !Filter.Wildcard.IsMatch(name))
return;
if (IncludeSubfolders && !fld.StartsWith(folder, StringComparison.OrdinalIgnoreCase))
return;
if (!IncludeSubfolders && string.Compare(folder, fld, StringComparison.OrdinalIgnoreCase) != 0)
return;
}
OnEventRecorded(this, new TaskEventArgs(taskEvent, TaskService));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"{nameof(Watcher_EventRecordWritten)} has failed. Error: {ex.ToString()}");
}
}
/// <summary>
/// Holds filter information for a <see cref="TaskEventWatcher"/>.
/// </summary>
[TypeConverter(typeof(ExpandableObjectConverter)), Serializable]
[PublicAPI]
public class EventFilter
{
private string filter = star;
private int[] ids;
private int[] levels;
private readonly TaskEventWatcher parent;
internal EventFilter([NotNull] TaskEventWatcher parent)
{
this.parent = parent;
}
/// <summary>
/// Gets or sets an optional array of event identifiers to use when filtering those events that will fire a <see cref="TaskEventWatcher.EventRecorded"/> event.
/// </summary>
/// <value>
/// The array of event identifier filters. All know task event identifiers are declared in the <see cref="StandardTaskEventId"/> enumeration.
/// </value>
[DefaultValue(null), Category("Filter"), Description("An array of event identifiers to use when filtering.")]
public int[] EventIds
{
get { return ids; }
set
{
if (ids != value)
{
ids = value;
parent.Restart();
}
}
}
/// <summary>
/// Gets or sets an optional array of event levels to use when filtering those events that will fire a <see cref="TaskEventWatcher.EventRecorded"/> event.
/// </summary>
/// <value>
/// The array of event levels. While event providers can define custom levels, most will use integers defined in the System.Diagnostics.Eventing.Reader.StandardEventLevel enumeration.
/// </value>
[DefaultValue(null), Category("Filter"), Description("An array of event levels to use when filtering.")]
public int[] EventLevels
{
get { return levels; }
set
{
if (levels != value)
{
levels = value;
parent.Restart();
}
}
}
/// <summary>
/// Gets or sets the task name, which can utilize wildcards, to look for when watching a folder.
/// </summary>
/// <value>A task name or wildcard.</value>
[DefaultValue(star), Category("Filter"), Description("A task name, which can utilize wildcards, for filtering.")]
public string TaskName
{
get { return filter; }
set
{
if (string.IsNullOrEmpty(value))
value = star;
if (string.Compare(filter, value, StringComparison.OrdinalIgnoreCase) != 0)
{
filter = value;
Wildcard = (value.IndexOfAny(new[] { '?', '*' }) == -1) ? null : new Wildcard(value);
parent.Restart();
}
}
}
internal Wildcard Wildcard { get; private set; } = new Wildcard(star);
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString() => filter + (levels == null ? "" : " +levels") + (ids == null ? "" : " +id's");
}
}
#if DESIGNER
namespace Design
{
internal class TaskEventWatcherDesigner : ComponentDesigner
{
public override void InitializeNewComponent(IDictionary defaultValues)
{
base.InitializeNewComponent(defaultValues);
var refs = GetService<IReferenceService>();
var tsColl = refs?.GetReferences(typeof(TaskService));
System.Diagnostics.Debug.Assert(refs != null && tsColl != null && tsColl.Length > 0, "Designer couldn't find host, reference service, or existing TaskService.");
if (tsColl != null && tsColl.Length > 0)
{
TaskEventWatcher tsComp = Component as TaskEventWatcher;
TaskService ts = tsColl[0] as TaskService;
if (tsComp != null)
tsComp.TaskService = ts;
}
}
protected virtual T GetService<T>() => (T)Component?.Site?.GetService(typeof(T));
}
}
#endif
}

View File

@ -1,12 +1,15 @@
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 winPEAS.TaskScheduler.V1;
using winPEAS.TaskScheduler.V2;
using System.Threading.Tasks;
using winPEAS.TaskScheduler.V1Interop;
using winPEAS.TaskScheduler.V2Interop;
namespace winPEAS.TaskScheduler
{
@ -43,12 +46,70 @@ namespace winPEAS.TaskScheduler
v1List = null;
}
/// <summary>
/// Gets a <see cref="System.Collections.Generic.IEnumerator{Task}"/> which enumerates all the tasks in this and all subfolders.
/// </summary>
/// <value>
/// A <see cref="System.Collections.Generic.IEnumerator{Task}"/> for all <see cref="Task"/> instances.
/// </value>
[NotNull, ItemNotNull]
public IEnumerable<Task> AllTasks => EnumerateFolderTasks(this);
/// <summary>
/// Gets the name that is used to identify the folder that contains a task.
/// </summary>
[NotNull]
public string Name => (v2Folder == null) ? rootString : v2Folder.Name;
/// <summary>
/// Gets the parent folder of this folder.
/// </summary>
/// <value>
/// The parent folder, or <c>null</c> if this folder is the root folder.
/// </value>
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);
}
}
/// <summary>
/// Gets the path to where the folder is stored.
/// </summary>
[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();
}
/// <summary>
/// Gets or sets the security descriptor of the task.
/// </summary>
/// <value>The security descriptor.</value>
[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); }
#pragma warning restore 0618
}
/// <summary>
/// Gets all the subfolders in the folder.
/// </summary>
@ -61,7 +122,8 @@ namespace winPEAS.TaskScheduler
{
if (v2Folder != null)
return new TaskFolderCollection(this, v2Folder.GetFolders(0));
} catch { }
}
catch { }
return new TaskFolderCollection();
}
}
@ -87,6 +149,28 @@ namespace winPEAS.TaskScheduler
/// </returns>
int IComparable<TaskFolder>.CompareTo(TaskFolder other) => string.Compare(Path, other.Path, true);
/// <summary>
/// Creates a folder for related tasks. Not available to Task Scheduler 1.0.
/// </summary>
/// <param name="subFolderName">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 <see cref="TaskFolder"/> 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.</param>
/// <param name="sd">The security descriptor associated with the folder.</param>
/// <returns>A <see cref="TaskFolder"/> instance that represents the new subfolder.</returns>
[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));
/// <summary>
/// Creates a folder for related tasks. Not available to Task Scheduler 1.0.
/// </summary>
/// <param name="subFolderName">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 <see cref="TaskFolder"/> 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.</param>
/// <param name="folderSecurity">The task security associated with the folder.</param>
/// <returns>A <see cref="TaskFolder"/> instance that represents the new subfolder.</returns>
public TaskFolder CreateFolder([NotNull] string subFolderName, [NotNull] TaskSecurity folderSecurity)
{
if (folderSecurity == null)
throw new ArgumentNullException(nameof(folderSecurity));
return CreateFolder(subFolderName, folderSecurity.GetSecurityDescriptorSddlForm(Task.defaultAccessControlSections));
}
/// <summary>
/// Creates a folder for related tasks. Not available to Task Scheduler 1.0.
/// </summary>
@ -96,7 +180,7 @@ namespace winPEAS.TaskScheduler
/// <returns>A <see cref="TaskFolder" /> instance that represents the new subfolder.</returns>
/// <exception cref="System.Security.SecurityException">Security descriptor mismatch between specified credentials and credentials on existing folder by same name.</exception>
/// <exception cref="System.ArgumentException">Invalid SDDL form.</exception>
/// <exception cref="NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
/// <exception cref="Microsoft.Win32.TaskScheduler.NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
public TaskFolder CreateFolder([NotNull] string subFolderName, string sddlForm = null, bool exceptionOnExists = true)
{
if (v2Folder == null) throw new NotV1SupportedException();
@ -138,7 +222,7 @@ namespace winPEAS.TaskScheduler
/// </summary>
/// <param name="subFolderName">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.</param>
/// <param name="exceptionOnNotExists">Set this value to false to avoid having an exception called if the folder does not exist.</param>
/// <exception cref="NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
/// <exception cref="Microsoft.Win32.TaskScheduler.NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
public void DeleteFolder([NotNull] string subFolderName, bool exceptionOnNotExists = true)
{
if (v2Folder != null)
@ -157,6 +241,52 @@ namespace winPEAS.TaskScheduler
throw new NotV1SupportedException();
}
/// <summary>Deletes a task from the folder.</summary>
/// <param name="name">
/// 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.
/// </param>
/// <param name="exceptionOnNotExists">Set this value to false to avoid having an exception called if the task does not exist.</param>
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;
}
}
/// <summary>Returns an enumerable collection of folders that matches a specified filter and recursion option.</summary>
/// <param name="filter">An optional predicate used to filter the returned <see cref="TaskFolder"/> instances.</param>
/// <returns>An enumerable collection of folders that matches <paramref name="filter"/>.</returns>
[NotNull, ItemNotNull]
public IEnumerable<TaskFolder> EnumerateFolders(Predicate<TaskFolder> filter = null)
{
foreach (var fld in SubFolders)
{
if (filter == null || filter(fld))
yield return fld;
}
}
/// <summary>Returns an enumerable collection of tasks that matches a specified filter and recursion option.</summary>
/// <param name="filter">An optional predicate used to filter the returned <see cref="Task"/> instances.</param>
/// <param name="recurse">Specifies whether the enumeration should include tasks in any subfolders.</param>
/// <returns>An enumerable collection of directories that matches <paramref name="filter"/> and <paramref name="recurse"/>.</returns>
[NotNull, ItemNotNull]
public IEnumerable<Task> EnumerateTasks(Predicate<Task> filter = null, bool recurse = false) => EnumerateFolderTasks(this, filter, recurse);
/// <summary>Determines whether the specified <see cref="System.Object"/>, is equal to this instance.</summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.</returns>
@ -168,11 +298,35 @@ namespace winPEAS.TaskScheduler
return false;
}
/// <summary>
/// Gets a <see cref="TaskSecurity"/> object that encapsulates the specified type of access control list (ACL) entries for the task described by the
/// current <see cref="TaskFolder"/> object.
/// </summary>
/// <returns>A <see cref="TaskSecurity"/> object that encapsulates the access control rules for the current folder.</returns>
[NotNull]
public TaskSecurity GetAccessControl() => GetAccessControl(Task.defaultAccessControlSections);
/// <summary>
/// Gets a <see cref="TaskSecurity"/> object that encapsulates the specified type of access control list (ACL) entries for the task folder described by
/// the current <see cref="TaskFolder"/> object.
/// </summary>
/// <param name="includeSections">
/// One of the <see cref="System.Security.AccessControl.AccessControlSections"/> values that specifies which group of access control entries to retrieve.
/// </param>
/// <returns>A <see cref="TaskSecurity"/> object that encapsulates the access control rules for the current folder.</returns>
[NotNull]
public TaskSecurity GetAccessControl(AccessControlSections includeSections) => new TaskSecurity(this, includeSections);
/// <summary>Returns a hash code for this instance.</summary>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode() => new { A = Path, B = TaskService.TargetServer, C = GetSecurityDescriptorSddlForm() }.GetHashCode();
/// <summary>Gets the security descriptor for the folder. Not available to Task Scheduler 1.0.</summary>
/// <param name="includeSections">Section(s) of the security descriptor to return.</param>
/// <returns>The security descriptor for the folder.</returns>
[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));
/// <summary>
/// Gets the security descriptor for the folder. Not available to Task Scheduler 1.0.
/// </summary>
@ -199,6 +353,60 @@ namespace winPEAS.TaskScheduler
return new TaskCollection(TaskService, filter);
}
/// <summary>Imports a <see cref="Task" /> from an XML file.</summary>
/// <param name="path">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.</param>
/// <param name="xmlFile">The file containing the XML-formatted definition of the task.</param>
/// <param name="overwriteExisting">If set to <see langword="true" />, overwrites any existing task with the same name.</param>
/// <returns>A <see cref="Task" /> instance that represents the new task.</returns>
/// <exception cref="NotV1SupportedException">Importing from an XML file is only supported under Task Scheduler 2.0.</exception>
public Task ImportTask(string path, [NotNull] string xmlFile, bool overwriteExisting = true) => RegisterTask(path, File.ReadAllText(xmlFile), overwriteExisting ? TaskCreation.CreateOrUpdate : TaskCreation.Create);
/// <summary>
/// Registers (creates) a new task in the folder using XML to define the task.
/// </summary>
/// <param name="path">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.</param>
/// <param name="xmlText">An XML-formatted definition of the task.</param>
/// <param name="createType">A union of <see cref="TaskCreation"/> flags.</param>
/// <param name="userId">The user credentials used to register the task.</param>
/// <param name="password">The password for the userId used to register the task.</param>
/// <param name="logonType">A <see cref="TaskLogonType"/> value that defines what logon technique is used to run the registered task.</param>
/// <param name="sddl">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.</param>
/// <returns>A <see cref="Task"/> instance that represents the new task.</returns>
/// <example><code lang="cs"><![CDATA[
/// // Define a basic task in XML
/// var xml = "<?xml version=\"1.0\" encoding=\"UTF-16\"?>" +
/// "<Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">" +
/// " <Principals>" +
/// " <Principal id=\"Author\">" +
/// " <UserId>S-1-5-18</UserId>" +
/// " </Principal>" +
/// " </Principals>" +
/// " <Triggers>" +
/// " <CalendarTrigger>" +
/// " <StartBoundary>2017-09-04T14:04:03</StartBoundary>" +
/// " <ScheduleByDay />" +
/// " </CalendarTrigger>" +
/// " </Triggers>" +
/// " <Actions Context=\"Author\">" +
/// " <Exec>" +
/// " <Command>cmd</Command>" +
/// " </Exec>" +
/// " </Actions>" +
/// "</Task>";
/// // Register the task in the root folder of the local machine using the SYSTEM account defined in XML
/// TaskService.Instance.RootFolder.RegisterTaskDefinition("Test", xml);
/// ]]></code></example>
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);
}
/// <summary>
/// Registers (creates) a task in a specified location using a <see cref="TaskDefinition"/> instance to define a task.
/// </summary>
@ -278,7 +486,7 @@ namespace winPEAS.TaskScheduler
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 = userId ?? definition.Principal.Account;
userId ??= definition.Principal.Account;
if (userId == string.Empty) userId = null;
User user = new User(userId);
if (v2Folder != null)
@ -331,7 +539,7 @@ namespace winPEAS.TaskScheduler
break;
case TaskLogonType.ServiceAccount:
flags &= ~(TaskFlags.Interactive | TaskFlags.RunOnlyIfLoggedOn);
definition.v1Task.SetAccountInformation((string.IsNullOrEmpty(userId) || user.IsSystem) ? String.Empty : user.Name, IntPtr.Zero);
definition.v1Task.SetAccountInformation((String.IsNullOrEmpty(userId) || user.IsSystem) ? String.Empty : user.Name, IntPtr.Zero);
break;
case TaskLogonType.InteractiveTokenOrPassword:
flags |= TaskFlags.Interactive;
@ -369,6 +577,34 @@ namespace winPEAS.TaskScheduler
return new Task(TaskService, definition.v1Task);
}
/// <summary>
/// Applies access control list (ACL) entries described by a <see cref="TaskSecurity"/> object to the file described by the current <see cref="TaskFolder"/> object.
/// </summary>
/// <param name="taskSecurity">A <see cref="TaskSecurity"/> object that describes an access control list (ACL) entry to apply to the current folder.</param>
public void SetAccessControl([NotNull] TaskSecurity taskSecurity) { taskSecurity.Persist(this); }
/// <summary>
/// Sets the security descriptor for the folder. Not available to Task Scheduler 1.0.
/// </summary>
/// <param name="sd">The security descriptor for the folder.</param>
/// <param name="includeSections">Section(s) of the security descriptor to set.</param>
[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)); }
/// <summary>
/// Sets the security descriptor for the folder. Not available to Task Scheduler 1.0.
/// </summary>
/// <param name="sddlForm">The security descriptor for the folder.</param>
/// <param name="options">Flags that specify how to set the security descriptor.</param>
/// <exception cref="NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
public void SetSecurityDescriptorSddlForm([NotNull] string sddlForm, TaskSetSecurityOptions options = TaskSetSecurityOptions.None)
{
if (v2Folder != null)
v2Folder.SetSecurityDescriptor(sddlForm, (int)options);
else
throw new NotV1SupportedException();
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>

View File

@ -1,71 +1,104 @@
using System;
using System.Collections.Generic;
using winPEAS.TaskScheduler.Native;
using winPEAS.TaskScheduler.V2;
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;
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Provides information and control for a collection of folders that contain tasks.
/// </summary>
public sealed class TaskFolderCollection : ICollection<TaskFolder>, IDisposable
/// <summary>Provides information and control for a collection of folders that contain tasks.</summary>
public sealed class TaskFolderCollection : ICollection<TaskFolder>, IDisposable, INotifyCollectionChanged, INotifyPropertyChanged
{
private const string IndexerName = "Item[]";
private readonly TaskFolder parent;
private readonly TaskFolder[] v1FolderList;
private readonly ITaskFolderCollection v2FolderList;
private readonly V2Interop.ITaskFolderCollection v2FolderList;
internal TaskFolderCollection()
{
v1FolderList = new TaskFolder[0];
}
internal TaskFolderCollection() => v1FolderList = new TaskFolder[0];
internal TaskFolderCollection([NotNull] TaskFolder folder, [NotNull] ITaskFolderCollection iCollection)
internal TaskFolderCollection([NotNull] TaskFolder folder, [NotNull] V2Interop.ITaskFolderCollection iCollection)
{
parent = folder;
v2FolderList = iCollection;
}
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
/// <summary>Occurs when a collection changes.</summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>Occurs when a property value changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Gets the number of items in the collection.</summary>
public int Count => v2FolderList?.Count ?? v1FolderList.Length;
/// <summary>
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
/// </summary>
/// <summary>Gets a value indicating whether the <see cref="ICollection{T}"/> is read-only.</summary>
bool ICollection<TaskFolder>.IsReadOnly => false;
/// <summary>
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/// <exception cref="System.NotImplementedException">This action is technically unfeasible due to limitations of the underlying library. Use the <see cref="TaskFolder.CreateFolder(string, string, bool)"/> instead.</exception>
public void Add([NotNull] TaskFolder item) { throw new NotImplementedException(); }
/// <summary>Gets the specified folder from the collection.</summary>
/// <param name="index">The index of the folder to be retrieved.</param>
/// <returns>A TaskFolder instance that represents the requested folder.</returns>
public TaskFolder this[int index]
{
get
{
if (v2FolderList != null)
return new TaskFolder(parent.TaskService, v2FolderList[++index]);
return v1FolderList[index];
}
}
/// <summary>
/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
/// <summary>Gets the specified folder from the collection.</summary>
/// <param name="path">The path of the folder to be retrieved.</param>
/// <returns>A TaskFolder instance that represents the requested folder.</returns>
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));
}
}
/// <summary>Adds an item to the <see cref="ICollection{T}"/>.</summary>
/// <param name="item">The object to add to the <see cref="ICollection{T}"/>.</param>
/// <exception cref="System.NotImplementedException">
/// This action is technically unfeasible due to limitations of the underlying library. Use the <see
/// cref="TaskFolder.CreateFolder(string, string, bool)"/> instead.
/// </exception>
public void Add([NotNull] TaskFolder item) => throw new NotImplementedException();
/// <summary>Removes all items from the <see cref="ICollection{T}"/>.</summary>
public void Clear()
{
if (v2FolderList != null)
{
for (int i = v2FolderList.Count; i > 0; i--)
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));
}
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
/// </summary>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/// <returns>
/// true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.
/// </returns>
/// <summary>Determines whether the <see cref="ICollection{T}"/> contains a specific value.</summary>
/// <param name="item">The object to locate in the <see cref="ICollection{T}"/>.</param>
/// <returns>true if <paramref name="item"/> is found in the <see cref="ICollection{T}"/>; otherwise, false.</returns>
public bool Contains([NotNull] TaskFolder item)
{
if (v2FolderList != null)
{
for (int i = v2FolderList.Count; i > 0; i--)
for (var i = v2FolderList.Count; i > 0; i--)
if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase))
return true;
}
@ -74,10 +107,11 @@ namespace winPEAS.TaskScheduler
return false;
}
/// <summary>
/// Copies the elements of the ICollection to an Array, starting at a particular Array index.
/// </summary>
/// <param name="array">The one-dimensional Array that is the destination of the elements copied from <see cref="ICollection{T}"/>. The Array must have zero-based indexing.</param>
/// <summary>Copies the elements of the ICollection to an Array, starting at a particular Array index.</summary>
/// <param name="array">
/// The one-dimensional Array that is the destination of the elements copied from <see cref="ICollection{T}"/>. The Array must have
/// zero-based indexing.
/// </param>
/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
public void CopyTo(TaskFolder[] array, int arrayIndex)
{
@ -98,9 +132,7 @@ namespace winPEAS.TaskScheduler
}
}
/// <summary>
/// Releases all resources used by this class.
/// </summary>
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
if (v1FolderList != null && v1FolderList.Length > 0)
@ -112,35 +144,77 @@ namespace winPEAS.TaskScheduler
System.Runtime.InteropServices.Marshal.ReleaseComObject(v2FolderList);
}
/// <summary>
/// Gets a list of items in a collection.
/// </summary>
/// <summary>Determines whether the specified folder exists.</summary>
/// <param name="path">The path of the folder.</param>
/// <returns>true if folder exists; otherwise, false.</returns>
public bool Exists([NotNull] string path)
{
try
{
parent.GetFolder(path);
return true;
}
catch { }
return false;
}
/// <summary>Gets a list of items in a collection.</summary>
/// <returns>Enumerated list of items in the collection.</returns>
public IEnumerator<TaskFolder> GetEnumerator()
{
if (v2FolderList != null)
return new ComEnumerator<TaskFolder, ITaskFolder>(() => v2FolderList.Count, (object o) => v2FolderList[o], o => new TaskFolder(parent.TaskService, o));
return new ComEnumerator<TaskFolder, V2Interop.ITaskFolder>(() => v2FolderList.Count, (object o) => v2FolderList[o], o => new TaskFolder(parent.TaskService, o));
return Array.AsReadOnly(v1FolderList).GetEnumerator();
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/*
/// <summary>Returns the index of the TaskFolder within the collection.</summary>
/// <param name="item">TaskFolder to find.</param>
/// <returns>Index of the TaskFolder; -1 if not found.</returns>
public int IndexOf(TaskFolder item)
{
return IndexOf(item.Path);
}
/// <summary>Returns the index of the TaskFolder within the collection.</summary>
/// <param name="path">Path to find.</param>
/// <returns>Index of the TaskFolder; -1 if not found.</returns>
public int IndexOf(string path)
{
if (v2FolderList != null)
{
for (int i = 0; i < v2FolderList.Count; i++)
{
if (v2FolderList[new System.Runtime.InteropServices.VariantWrapper(i)].Path == path)
return i;
}
return -1;
}
else
return (v1FolderList.Length > 0 && (path == string.Empty || path == "\\")) ? 0 : -1;
}
*/
/// <summary>Removes the first occurrence of a specific object from the <see cref="ICollection{T}"/>.</summary>
/// <param name="item">The object to remove from the <see cref="ICollection{T}"/>.</param>
/// <returns>
/// true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
/// true if <paramref name="item"/> was successfully removed from the <see cref="ICollection{T}"/>; otherwise, false. This method
/// also returns false if <paramref name="item"/> is not found in the original <see cref="ICollection{T}"/>.
/// </returns>
public bool Remove([NotNull] TaskFolder item)
{
if (v2FolderList != null)
{
for (int i = v2FolderList.Count; i > 0; i--)
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
{
@ -154,5 +228,9 @@ namespace winPEAS.TaskScheduler
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>Called when a property has changed to notify any attached elements.</summary>
/// <param name="propertyName">Name of the property.</param>
private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@ -1,8 +1,41 @@
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Defines the methods that are called by the Task Scheduler service to manage a COM handler.
/// </summary>
/// <remarks>
/// 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 <see cref="ComHandlerAction"/> class.
/// </remarks>
[ComImport, Guid("839D7762-5121-4009-9234-4F0D19394F04"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity]
public interface ITaskHandler
{
/// <summary>
/// Called to start the COM handler. This method must be implemented by the handler.
/// </summary>
/// <param name="pHandlerServices">An <c>IUnkown</c> interface that is used to communicate back with the Task Scheduler.</param>
/// <param name="data">The arguments that are required by the handler. These arguments are defined in the <see cref="ComHandlerAction.Data"/> property of the COM handler action.</param>
void Start([In, MarshalAs(UnmanagedType.IUnknown)] object pHandlerServices, [In, MarshalAs(UnmanagedType.BStr)] string data);
/// <summary>
/// Called to stop the COM handler. This method must be implemented by the handler.
/// </summary>
/// <param name="pRetCode">The return code that the Task Schedule will raise as an event when the COM handler action is completed.</param>
void Stop([MarshalAs(UnmanagedType.Error)] out int pRetCode);
/// <summary>
/// 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.
/// </summary>
void Pause();
/// <summary>
/// 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.
/// </summary>
void Resume();
}
/// <summary>
/// Provides the methods that are used by COM handlers to notify the Task Scheduler about the status of the handler.

View File

@ -2,7 +2,6 @@
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;
using winPEAS.TaskScheduler.Native;
namespace winPEAS.TaskScheduler
{
@ -66,6 +65,17 @@ namespace winPEAS.TaskScheduler
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskAccessRule"/> 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.
/// </summary>
/// <param name="identity">The name of the user or group the rule applies to.</param>
/// <param name="eventRights">A bitwise combination of <see cref="TaskRights"/> values specifying the rights allowed or denied.</param>
/// <param name="type">One of the <see cref="AccessControlType"/> values specifying whether the rights are allowed or denied.</param>
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)
{
@ -85,6 +95,17 @@ namespace winPEAS.TaskScheduler
/// </summary>
public sealed class TaskAuditRule : AuditRule
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskAuditRule" /> class, specifying the user or group to audit, the rights to audit, and whether to audit success, failure, or both.
/// </summary>
/// <param name="identity">The user or group the rule applies to. Must be of type <see cref="SecurityIdentifier" /> or a type such as <see cref="NTAccount" /> that can be converted to type <see cref="SecurityIdentifier" />.</param>
/// <param name="eventRights">A bitwise combination of <see cref="TaskRights" /> values specifying the kinds of access to audit.</param>
/// <param name="flags">The audit flags.</param>
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)
{
@ -149,7 +170,17 @@ namespace winPEAS.TaskScheduler
this.CanonicalizeAccessRules();
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskSecurity" /> class with the specified sections of the access control security rules from the specified task.
/// </summary>
/// <param name="folder">The folder.</param>
/// <param name="sections">The sections of the ACL to retrieve.</param>
public TaskSecurity([NotNull] TaskFolder folder, AccessControlSections sections = Task.defaultAccessControlSections)
: base(false)
{
SetSecurityDescriptorSddlForm(folder.GetSecurityDescriptorSddlForm(Convert(sections)), sections);
this.CanonicalizeAccessRules();
}
/// <summary>
/// Gets the enumeration that the <see cref="TaskSecurity"/> class uses to represent access rights.
@ -169,6 +200,24 @@ namespace winPEAS.TaskScheduler
/// <returns>A <see cref="Type"/> object representing the <see cref="TaskAuditRule"/> class.</returns>
public override Type AuditRuleType => typeof(TaskAuditRule);
/// <summary>
/// Gets a <see cref="TaskSecurity"/> object that represent the default access rights.
/// </summary>
/// <value>The default task security.</value>
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;
}
}
/// <summary>
/// Creates a new access control rule for the specified user, with the specified access rights, access control, and flags.
/// </summary>
@ -183,6 +232,24 @@ namespace winPEAS.TaskScheduler
/// </returns>
public override AccessRule AccessRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) => new TaskAccessRule(identityReference, (TaskRights)accessMask, type);
/// <summary>
/// Searches for a matching rule with which the new rule can be merged. If none are found, adds the new rule.
/// </summary>
/// <param name="rule">The access control rule to add.</param>
public void AddAccessRule([NotNull] TaskAccessRule rule)
{
base.AddAccessRule(rule);
}
/// <summary>
/// Searches for an audit rule with which the new rule can be merged. If none are found, adds the new rule.
/// </summary>
/// <param name="rule">The audit rule to add. The user specified by this rule determines the search.</param>
public void AddAuditRule([NotNull] TaskAuditRule rule)
{
base.AddAuditRule(rule);
}
/// <summary>
/// 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.
/// </summary>
@ -197,6 +264,83 @@ namespace winPEAS.TaskScheduler
/// </returns>
public override AuditRule AuditRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) => new TaskAuditRule(identityReference, accessMask, isInherited, inheritanceFlags, propagationFlags, flags);
/// <summary>
/// Searches for an access control rule with the same user and <see cref="AccessControlType"/> (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.
/// </summary>
/// <param name="rule">A <see cref="TaskAccessRule"/> that specifies the user and <see cref="AccessControlType"/> 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.</param>
/// <returns><c>true</c> if a compatible rule is found; otherwise <c>false</c>.</returns>
public bool RemoveAccessRule([NotNull] TaskAccessRule rule) => base.RemoveAccessRule(rule);
/// <summary>
/// Searches for all access control rules with the same user and <see cref="AccessControlType"/> (allow or deny) as the specified rule and, if found, removes them.
/// </summary>
/// <param name="rule">A <see cref="TaskAccessRule"/> that specifies the user and <see cref="AccessControlType"/> 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.</param>
public void RemoveAccessRuleAll([NotNull] TaskAccessRule rule)
{
base.RemoveAccessRuleAll(rule);
}
/// <summary>
/// Searches for an access control rule that exactly matches the specified rule and, if found, removes it.
/// </summary>
/// <param name="rule">The <see cref="TaskAccessRule"/> to remove.</param>
public void RemoveAccessRuleSpecific([NotNull] TaskAccessRule rule)
{
base.RemoveAccessRuleSpecific(rule);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="rule">A <see cref="TaskAuditRule"/> 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.</param>
/// <returns><c>true</c> if a compatible rule is found; otherwise <c>false</c>.</returns>
public bool RemoveAuditRule([NotNull] TaskAuditRule rule) => base.RemoveAuditRule(rule);
/// <summary>
/// Searches for all audit rules with the same user as the specified rule and, if found, removes them.
/// </summary>
/// <param name="rule">A <see cref="TaskAuditRule"/> that specifies the user to search for. Any rights specified by this rule are ignored.</param>
public void RemoveAuditRuleAll(TaskAuditRule rule)
{
base.RemoveAuditRuleAll(rule);
}
/// <summary>
/// Searches for an audit rule that exactly matches the specified rule and, if found, removes it.
/// </summary>
/// <param name="rule">The <see cref="TaskAuditRule"/> to remove.</param>
public void RemoveAuditRuleSpecific([NotNull] TaskAuditRule rule)
{
base.RemoveAuditRuleSpecific(rule);
}
/// <summary>
/// Removes all access control rules with the same user as the specified rule, regardless of <see cref="AccessControlType"/>, and then adds the specified rule.
/// </summary>
/// <param name="rule">The <see cref="TaskAccessRule"/> to add. The user specified by this rule determines the rules to remove before this rule is added.</param>
public void ResetAccessRule([NotNull] TaskAccessRule rule)
{
base.ResetAccessRule(rule);
}
/// <summary>
/// Removes all access control rules with the same user and <see cref="AccessControlType"/> (allow or deny) as the specified rule, and then adds the specified rule.
/// </summary>
/// <param name="rule">The <see cref="TaskAccessRule"/> to add. The user and <see cref="AccessControlType"/> of this rule determine the rules to remove before this rule is added.</param>
public void SetAccessRule([NotNull] TaskAccessRule rule)
{
base.SetAccessRule(rule);
}
/// <summary>
/// Removes all audit rules with the same user as the specified rule, regardless of the <see cref="AuditFlags"/> value, and then adds the specified rule.
/// </summary>
/// <param name="rule">The <see cref="TaskAuditRule"/> to add. The user specified by this rule determines the rules to remove before this rule is added.</param>
public void SetAuditRule([NotNull] TaskAuditRule rule)
{
base.SetAuditRule(rule);
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
@ -219,6 +363,20 @@ namespace winPEAS.TaskScheduler
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;
@ -264,5 +422,43 @@ namespace winPEAS.TaskScheduler
WriteUnlock();
}
}
/// <summary>
/// Saves the specified sections of the security descriptor associated with this <see cref="TaskSecurity" /> object to permanent storage. We recommend that the values of the <paramref name="includeSections" /> parameters passed to the constructor and persist methods be identical.
/// </summary>
/// <param name="folder">The task folder used to retrieve the persisted information.</param>
/// <param name="includeSections">One of the <see cref="AccessControlSections" /> enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save.</param>
[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();
}
}
/// <summary>
/// Saves the specified sections of the security descriptor associated with this <see cref="T:System.Security.AccessControl.ObjectSecurity" /> object to permanent storage. We recommend that the values of the <paramref name="includeSections" /> parameters passed to the constructor and persist methods be identical. For more information, see Remarks.
/// </summary>
/// <param name="name">The name used to retrieve the persisted information.</param>
/// <param name="includeSections">One of the <see cref="T:System.Security.AccessControl.AccessControlSections" /> enumeration values that specifies the sections of the security descriptor (access rules, audit rules, owner, primary group) of the securable object to save.</param>
protected override void Persist([NotNull] string name, AccessControlSections includeSections = Task.defaultAccessControlSections)
{
using (var ts = new TaskService())
{
var task = ts.GetTask(name);
Persist(task, includeSections);
}
}
}
}

View File

@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using winPEAS.TaskScheduler.Native;
using winPEAS.TaskScheduler.V1;
using winPEAS.TaskScheduler.V2;
using System.Text;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Quick simple trigger types for the
/// <see cref="TaskService.AddTask"/> method.
/// <see cref="TaskService.AddTask(string,Microsoft.Win32.TaskScheduler.Trigger,Microsoft.Win32.TaskScheduler.Action,string,string,Microsoft.Win32.TaskScheduler.TaskLogonType,string)"/> method.
/// </summary>
public enum QuickTriggerType
{
@ -77,14 +78,14 @@ namespace winPEAS.TaskScheduler
{
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;
private static Guid CLSID_Ctask = typeof(V1Interop.CTask).GUID;
private static Guid IID_ITask = typeof(V1Interop.ITask).GUID;
[ThreadStatic]
private static TaskService instance;
private static Version osLibVer;
internal ITaskScheduler v1TaskScheduler;
internal ITaskService v2TaskService;
internal V1Interop.ITaskScheduler v1TaskScheduler;
internal V2Interop.ITaskService v2TaskService;
private bool connecting;
private bool forceV1;
private bool initializing;
@ -131,6 +132,18 @@ namespace winPEAS.TaskScheduler
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();
}
/// <summary>Delegate for methods that support update calls during COM handler execution.</summary>
/// <param name="percentage">The percentage of completion (0 to 100).</param>
/// <param name="message">An optional message.</param>
@ -205,6 +218,40 @@ namespace winPEAS.TaskScheduler
[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; }
/// <summary>Gets the name of the domain to which the <see cref="TargetServer"/> computer is connected.</summary>
[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;
}
}
/// <summary>Gets the name of the user that is connected to the Task Scheduler service.</summary>
[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];
}
}
/// <summary>Gets the highest version of Task Scheduler that a computer supports.</summary>
/// <remarks>
/// The following table list the various versions and their host operating system:
@ -331,10 +378,106 @@ namespace winPEAS.TaskScheduler
}
}
/// <summary>Gets a <see cref="System.Collections.Generic.IEnumerator{T}"/> which enumerates all the tasks in all folders.</summary>
/// <value>A <see cref="System.Collections.Generic.IEnumerator{T}"/> for all <see cref="Task"/> instances.</value>
[Browsable(false)]
public System.Collections.Generic.IEnumerable<Task> AllTasks => RootFolder.AllTasks;
/// <summary>Gets a Boolean value that indicates if you are connected to the Task Scheduler service.</summary>
[Browsable(false)]
public bool Connected => v2TaskService != null && v2TaskService.Connected || v1TaskScheduler != null;
/// <summary>
/// Gets the connection token for this <see cref="TaskService"/> instance. This token is thread safe and can be used to create new
/// <see cref="TaskService"/> instances on other threads using the <see cref="CreateFromToken"/> static method.
/// </summary>
/// <value>The connection token.</value>
public ConnectionToken Token =>
ConnectionDataManager.TokenFromInstance(TargetServer, UserName, UserAccountDomain, UserPassword, forceV1);
/// <summary>Gets a value indicating whether the component can raise an event.</summary>
protected override bool CanRaiseEvents { get; } = false;
/// <summary>
/// Creates a new <see cref="TaskService"/> 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.
/// </summary>
/// <param name="token">The token.</param>
/// <returns>A <see cref="TaskService"/> instance valid for the thread calling this method.</returns>
public static TaskService CreateFromToken(ConnectionToken token) => ConnectionDataManager.InstanceFromToken(token);
/// <summary>Gets a formatted string that tells the Task Scheduler to retrieve a string from a resource .dll file.</summary>
/// <param name="dllPath">The path to the .dll file that contains the resource.</param>
/// <param name="resourceId">The identifier for the resource text (typically a negative number).</param>
/// <returns>A string in the format of $(@ [dllPath], [resourceId]).</returns>
/// <example>
/// 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.
/// </example>
public static string GetDllResourceString([NotNull] string dllPath, int resourceId) => $"$(@ {dllPath}, {resourceId})";
/// <summary>
/// Runs an action that is defined via a COM handler. COM CLSID must be registered to an object that implements the
/// <see cref="ITaskHandler"/> interface.
/// </summary>
/// <param name="clsid">The CLSID of the COM object.</param>
/// <param name="data">An optional string passed to the COM object at startup.</param>
/// <param name="millisecondsTimeout">The number of milliseconds to wait or -1 for indefinitely.</param>
/// <param name="onUpdate">
/// An optional <see cref="ComHandlerUpdate"/> delegate that is called when the COM object calls the
/// <see cref="ITaskHandlerStatus.UpdateStatus(short, string)"/> method.
/// </param>
/// <returns>The value set by the COM object via a call to the <see cref="ITaskHandlerStatus.TaskCompleted(int)"/> method.</returns>
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;
}
/// <summary>
/// Runs an action that is defined via a COM handler. COM CLSID must be registered to an object that implements the
/// <see cref="ITaskHandler"/> interface.
/// </summary>
/// <param name="clsid">The CLSID of the COM object.</param>
/// <param name="onComplete">The action to run on thread completion.</param>
/// <param name="data">An optional string passed to the COM object at startup.</param>
/// <param name="millisecondsTimeout">The number of milliseconds to wait or -1 for indefinitely.</param>
/// <param name="onUpdate">
/// An optional <see cref="ComHandlerUpdate"/> delegate that is called when the COM object calls the
/// <see cref="ITaskHandlerStatus.UpdateStatus(short, string)"/> method.
/// </param>
public static void RunComHandlerActionAsync(Guid clsid, Action<int> onComplete, string data = null, int millisecondsTimeout = -1, ComHandlerUpdate onUpdate = null) => new ComHandlerThread(clsid, data, millisecondsTimeout, onUpdate, onComplete).Start();
/// <summary>Adds or updates an Automatic Maintenance Task on the connected machine.</summary>
/// <param name="taskPathAndName">Name of the task with full path.</param>
/// <param name="period">The amount of time the task needs once executed during regular Automatic maintenance.</param>
/// <param name="deadline">
/// 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.
/// </param>
/// <param name="executablePath">The path to an executable file.</param>
/// <param name="arguments">The arguments associated with the command-line operation.</param>
/// <param name="workingDirectory">
/// The directory that contains either the executable file or the files that are used by the executable file.
/// </param>
/// <returns>A <see cref="Task"/> instance of the Automatic Maintenance Task.</returns>
/// <exception cref="System.InvalidOperationException">
/// Automatic Maintenance tasks are only supported on Windows 8/Server 2012 and later.
/// </exception>
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)");
}
/// <summary>Creates a new task, registers the task, and returns the instance.</summary>
/// <param name="path">
/// 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
@ -377,6 +520,75 @@ namespace winPEAS.TaskScheduler
return RootFolder.RegisterTaskDefinition(path, td, TaskCreation.CreateOrUpdate, userId, password, logonType);
}
/// <summary>Creates a new task, registers the task, and returns the instance.</summary>
/// <param name="path">
/// 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.
/// </param>
/// <param name="trigger">The <see cref="Trigger"/> to determine when to run the task.</param>
/// <param name="exePath">The executable path.</param>
/// <param name="arguments">The arguments (optional). Value can be NULL.</param>
/// <param name="userId">The user credentials used to register the task.</param>
/// <param name="password">The password for the userId used to register the task.</param>
/// <param name="logonType">
/// A <see cref="TaskLogonType"/> value that defines what logon technique is used to run the registered task.
/// </param>
/// <param name="description">The task description.</param>
/// <returns>A <see cref="Task"/> instance of the registered task.</returns>
/// <example>
/// <code lang="cs">
/// <![CDATA[
/// // Display a log file every day
/// TaskService.Instance.AddTask("Test", QuickTriggerType.Daily, "notepad.exe", "c:\\test.log"));
/// ]]>
/// </code>
/// </example>
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 ExecAction(exePath, arguments), userId, password, logonType, description);
}
/// <summary>Signals the object that initialization is starting.</summary>
public void BeginInit() => initializing = true;
@ -398,6 +610,46 @@ namespace winPEAS.TaskScheduler
return base.Equals(obj);
}
/// <summary>Finds all tasks matching a name or standard wildcards.</summary>
/// <param name="name">Name of the task in regular expression form.</param>
/// <param name="searchAllFolders">if set to <c>true</c> search all sub folders.</param>
/// <returns>An array of <see cref="Task"/> containing all tasks matching <paramref name="name"/>.</returns>
public Task[] FindAllTasks(System.Text.RegularExpressions.Regex name, bool searchAllFolders = true)
{
var results = new System.Collections.Generic.List<Task>();
FindTaskInFolder(RootFolder, name, ref results, searchAllFolders);
return results.ToArray();
}
/// <summary>Finds all tasks matching a name or standard wildcards.</summary>
/// <param name="filter">The filter used to determine tasks to select.</param>
/// <param name="searchAllFolders">if set to <c>true</c> search all sub folders.</param>
/// <returns>An array of <see cref="Task"/> containing all tasks matching <paramref name="filter"/>.</returns>
public Task[] FindAllTasks(Predicate<Task> filter, bool searchAllFolders = true)
{
if (filter == null) filter = t => true;
var results = new System.Collections.Generic.List<Task>();
FindTaskInFolder(RootFolder, filter, ref results, searchAllFolders);
return results.ToArray();
}
/// <summary>Finds a task given a name and standard wildcards.</summary>
/// <param name="name">The task name. This can include the wildcards * or ?.</param>
/// <param name="searchAllFolders">if set to <c>true</c> search all sub folders.</param>
/// <returns>A <see cref="Task"/> if one matches <paramref name="name"/>, otherwise NULL.</returns>
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;
}
/// <summary>Gets the event log for this <see cref="TaskService"/> instance.</summary>
/// <param name="taskPath">(Optional) The task path if only the events for a single task are desired.</param>
/// <returns>A <see cref="TaskEventLog"/> instance.</returns>
public TaskEventLog GetEventLog(string taskPath = null) => new TaskEventLog(TargetServer, taskPath, UserAccountDomain, UserName, UserPassword);
/// <summary>Gets the path to a folder of registered tasks.</summary>
/// <param name="folderName">
/// 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
@ -434,6 +686,44 @@ namespace winPEAS.TaskScheduler
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode() => new { A = TargetServer, B = UserAccountDomain, C = UserName, D = UserPassword, E = forceV1 }.GetHashCode();
/// <summary>Gets a collection of running tasks.</summary>
/// <param name="includeHidden">True to include hidden tasks.</param>
/// <returns><see cref="RunningTaskCollection"/> instance with the list of running tasks.</returns>
public RunningTaskCollection GetRunningTasks(bool includeHidden = true)
{
if (v2TaskService != null)
try
{
return new RunningTaskCollection(this, v2TaskService.GetRunningTasks(includeHidden ? 1 : 0));
}
catch { }
return new RunningTaskCollection(this);
}
/// <summary>Gets the task with the specified path.</summary>
/// <param name="taskPath">The task path.</param>
/// <returns>
/// The <see cref="Task"/> instance matching the <paramref name="taskPath"/>, if found. If not found, this method returns <c>null</c>.
/// </returns>
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;
}
/// <summary>
/// Returns an empty task definition object to be filled in with settings and properties and then registered using the
/// <see cref="TaskFolder.RegisterTaskDefinition(string, TaskDefinition)"/> method.
@ -447,6 +737,23 @@ namespace winPEAS.TaskScheduler
return new TaskDefinition(v1TaskScheduler.NewWorkItem(v1Name, CLSID_Ctask, IID_ITask), v1Name);
}
/// <summary>Returns a <see cref="TaskDefinition"/> populated with the properties defined in an XML file.</summary>
/// <param name="xmlFile">The XML file to use as input.</param>
/// <returns>A <see cref="TaskDefinition"/> instance.</returns>
/// <exception cref="NotV1SupportedException">Importing from an XML file is only supported under Task Scheduler 2.0.</exception>
public TaskDefinition NewTaskFromFile([NotNull] string xmlFile)
{
var td = NewTask();
td.XmlText = System.IO.File.ReadAllText(xmlFile);
return td;
}
/// <summary>Starts the Task Scheduler UI for the OS hosting the assembly if the session is running in interactive mode.</summary>
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)
@ -458,9 +765,9 @@ namespace winPEAS.TaskScheduler
info.AddValue("forceV1", forceV1, typeof(bool));
}
internal static IRegisteredTask GetTask([NotNull] ITaskService iSvc, [NotNull] string name)
internal static V2Interop.IRegisteredTask GetTask([NotNull] V2Interop.ITaskService iSvc, [NotNull] string name)
{
ITaskFolder fld = null;
V2Interop.ITaskFolder fld = null;
try
{
fld = iSvc.GetFolder("\\");
@ -476,7 +783,7 @@ namespace winPEAS.TaskScheduler
}
}
internal static ITask GetTask([NotNull] ITaskScheduler iSvc, [NotNull] string name)
internal static V1Interop.ITask GetTask([NotNull] V1Interop.ITaskScheduler iSvc, [NotNull] string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
@ -584,7 +891,7 @@ namespace winPEAS.TaskScheduler
if (LibraryIsV2 && !forceV1)
{
v2TaskService = new ITaskService();
v2TaskService = new V2Interop.ITaskService();
if (!string.IsNullOrEmpty(targetServer))
{
// Check to ensure character only server name. (Suggested by bigsan)
@ -605,7 +912,7 @@ namespace winPEAS.TaskScheduler
else
{
v1Impersonation = new WindowsImpersonatedIdentity(userName, userDomain, userPassword);
v1TaskScheduler = new ITaskScheduler();
v1TaskScheduler = new V1Interop.ITaskScheduler();
if (!string.IsNullOrEmpty(targetServer))
{
// Check to ensure UNC format for server name. (Suggested by bigsan)
@ -696,6 +1003,8 @@ namespace winPEAS.TaskScheduler
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);
@ -713,19 +1022,102 @@ namespace winPEAS.TaskScheduler
internal ConnectionToken(int value) => token = value;
}
// Manages the list of tokens and associated data
private static class ConnectionDataManager
{
public static List<ConnectionData> connections = new List<ConnectionData>() { 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<int> 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<int> OnCompleted;
private readonly ComHandlerUpdate OnUpdate;
public TaskHandlerStatus(Action<int> 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);
@ -738,6 +1130,15 @@ namespace winPEAS.TaskScheduler
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) &&
@ -747,6 +1148,18 @@ namespace winPEAS.TaskScheduler
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);
}
}
}
}

View File

@ -1,69 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace winPEAS.TaskScheduler
{
public abstract partial class Trigger
{
internal class CronExpression
{
private FieldVal[] Fields = new FieldVal[5];
private CronExpression() { }
public enum CronFieldType { Minutes, Hours, Days, Months, DaysOfWeek };
public struct FieldVal
{
private const string rangeRegEx = @"^(?:(?<A>\*)|(?<D1>\d+)(?:-(?<D2>\d+))?)(?:\/(?<I>\d+))?$";
private readonly static Dictionary<string, string> dow = new Dictionary<string, string>(7)
{
{ "SUN", "0" },
{ "MON", "1" },
{ "TUE", "2" },
{ "WED", "3" },
{ "THU", "4" },
{ "FRI", "5" },
{ "SAT", "6" },
};
private readonly static Dictionary<string, string> mon = new Dictionary<string, string>(12)
{
{ "JAN", "1" },
{ "FEB", "2" },
{ "MAR", "3" },
{ "APR", "4" },
{ "MAY", "5" },
{ "JUN", "6" },
{ "JUL", "7" },
{ "AUG", "8" },
{ "SEP", "9" },
{ "OCT", "10" },
{ "NOV", "11" },
{ "DEC", "12" },
};
private readonly static Dictionary<CronFieldType, MinMax> validRange = new Dictionary<CronFieldType, MinMax>(5)
{
{ CronFieldType.Days, new MinMax(1, 31) },
{ CronFieldType.DaysOfWeek, new MinMax(0, 6) },
{ CronFieldType.Hours, new MinMax(0, 23) },
{ CronFieldType.Minutes, new MinMax(0, 59) },
{ CronFieldType.Months, new MinMax(1, 12) },
};
private CronFieldType cft;
private FieldFlags flags;
private int incr;
private int[] vals;
enum FieldFlags { List, Every, Range, Increment };
public override string ToString() => $"Type:{flags}; Vals:{string.Join(",", vals.Select(i => i.ToString()).ToArray())}; Incr:{incr}";
private struct MinMax
{
public int Min, Max;
public MinMax(int min, int max) { Min = min; Max = max; }
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +1,105 @@
using System;
using System.Collections;
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.Native;
using winPEAS.TaskScheduler.V1;
using winPEAS.TaskScheduler.V2;
using winPEAS.TaskScheduler.TaskEditor.Native;
using winPEAS.TaskScheduler.V1Interop;
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Provides the methods that are used to add to, remove from, and get the triggers of a task.
/// </summary>
[XmlRoot("Triggers", Namespace = TaskDefinition.tns, IsNullable = false)]
public sealed class TriggerCollection : IList<Trigger>, IDisposable, IXmlSerializable, IList
public sealed class TriggerCollection : IList<Trigger>, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
private ITask v1Task;
private readonly ITriggerCollection v2Coll;
private ITaskDefinition v2Def;
private const string IndexerName = "Item[]";
private readonly V2Interop.ITriggerCollection v2Coll;
private bool inV2set;
private V1Interop.ITask v1Task;
private V2Interop.ITaskDefinition v2Def;
internal TriggerCollection([NotNull] ITask iTask)
{
v1Task = iTask;
}
internal TriggerCollection([NotNull] V1Interop.ITask iTask) => v1Task = iTask;
internal TriggerCollection([NotNull] ITaskDefinition iTaskDef)
internal TriggerCollection([NotNull] V2Interop.ITaskDefinition iTaskDef)
{
v2Def = iTaskDef;
v2Coll = v2Def.Triggers;
}
/// <summary>
/// Gets the number of triggers in the collection.
/// </summary>
/// <summary>Occurs when a collection changes.</summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>Occurs when a property value changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Gets the number of triggers in the collection.</summary>
public int Count => v2Coll?.Count ?? v1Task.GetTriggerCount();
bool IList.IsFixedSize => false;
bool ICollection<Trigger>.IsReadOnly => false;
bool IList.IsReadOnly => false;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => this;
bool ICollection<Trigger>.IsReadOnly => false;
bool IList.IsFixedSize => false;
bool IList.IsReadOnly => false;
object IList.this[int index]
/// <summary>Gets or sets a specified trigger from the collection.</summary>
/// <value>The <see cref="Trigger"/>.</value>
/// <param name="triggerId">The id ( <see cref="Trigger.Id"/>) of the trigger to be retrieved.</param>
/// <returns>Specialized <see cref="Trigger"/> instance.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="NullReferenceException"></exception>
/// <exception cref="InvalidOperationException">Mismatching Id for trigger and lookup.</exception>
public Trigger this[[NotNull] string triggerId]
{
get { return this[index]; }
set { this[index] = (Trigger)value; }
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);
}
}
/// <summary>
/// Gets a specified trigger from the collection.
/// </summary>
/// <summary>Gets a specified trigger from the collection.</summary>
/// <param name="index">The index of the trigger to be retrieved.</param>
/// <returns>Specialized <see cref="Trigger"/> instance.</returns>
public Trigger this[int index]
@ -67,21 +112,35 @@ namespace winPEAS.TaskScheduler
}
set
{
if (Count <= index)
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));
}
}
/// <summary>
/// Add an unbound <see cref="Trigger"/> to the task.
/// </summary>
/// <typeparam name="TTrigger">A type derived from <see cref="Trigger"/>.</typeparam>
/// <param name="unboundTrigger"><see cref="Trigger"/> derivative to add to the task.</param>
/// <returns>Bound trigger.</returns>
/// <exception cref="System.ArgumentNullException"><c>unboundTrigger</c> is <c>null</c>.</exception>
public TTrigger Add<TTrigger>([NotNull] TTrigger unboundTrigger) where TTrigger : Trigger
object IList.this[int index]
{
get => this[index];
set => this[index] = (Trigger)value;
}
/*/// <summary>
/// Add an unbound <see cref="Trigger"/> to the task. </summary> <param name="unboundTrigger"><see cref="Trigger"/> derivative to
/// add to the task.</param> <returns>Bound trigger.</returns> <exception cref="System.ArgumentNullException"><c>unboundTrigger</c>
/// is <c>null</c>.</exception>
public Trigger Add([NotNull] Trigger unboundTrigger)
{
if (unboundTrigger == null)
throw new ArgumentNullException(nameof(unboundTrigger));
@ -90,64 +149,112 @@ namespace winPEAS.TaskScheduler
else
unboundTrigger.Bind(v1Task);
return unboundTrigger;
}*/
/// <summary>Add an unbound <see cref="Trigger"/> to the task.</summary>
/// <typeparam name="TTrigger">A type derived from <see cref="Trigger"/>.</typeparam>
/// <param name="unboundTrigger"><see cref="Trigger"/> derivative to add to the task.</param>
/// <returns>Bound trigger.</returns>
/// <exception cref="ArgumentNullException"><c>unboundTrigger</c> is <c>null</c>.</exception>
public TTrigger Add<TTrigger>([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;
}
/// <summary>
/// Add a new trigger to the collections of triggers for the task.
/// </summary>
/// <summary>Add a new trigger to the collections of triggers for the task.</summary>
/// <param name="taskTriggerType">The type of trigger to create.</param>
/// <returns>A <see cref="Trigger"/> instance of the specified type.</returns>
public Trigger AddNew(TaskTriggerType taskTriggerType)
{
if (v1Task != null)
{
ushort idx;
return Trigger.CreateTrigger(v1Task.CreateTrigger(out idx), Trigger.ConvertToV1TriggerType(taskTriggerType));
}
return Trigger.CreateTrigger(v1Task.CreateTrigger(out _), Trigger.ConvertToV1TriggerType(taskTriggerType));
return Trigger.CreateTrigger(v2Coll.Create(taskTriggerType), v2Def);
}
/// <summary>
/// Clears all triggers from the task.
/// </summary>
/// <summary>Adds a collection of unbound triggers to the end of the <see cref="TriggerCollection"/>.</summary>
/// <param name="triggers">
/// The triggers to be added to the end of the <see cref="TriggerCollection"/>. The collection itself cannot be <c>null</c> and
/// cannot contain <c>null</c> elements.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="triggers"/> is <c>null</c>.</exception>
public void AddRange([NotNull] IEnumerable<Trigger> triggers)
{
if (triggers == null)
throw new ArgumentNullException(nameof(triggers));
foreach (var item in triggers)
Add(item);
}
/// <summary>Clears all triggers from the task.</summary>
public void Clear()
{
if (v2Coll != null)
v2Coll.Clear();
else
{
for (int i = Count - 1; i >= 0; i--)
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));
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
/// </summary>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/// <returns>
/// true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.
/// </returns>
/// <summary>Determines whether the <see cref="ICollection{T}"/> contains a specific value.</summary>
/// <param name="item">The object to locate in the <see cref="ICollection{T}"/>.</param>
/// <returns>true if <paramref name="item"/> is found in the <see cref="ICollection{T}"/>; otherwise, false.</returns>
public bool Contains([NotNull] Trigger item) => Find(a => a.Equals(item)) != null;
/// <summary>
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1" /> to an <see cref="Array"/>, starting at a particular <see cref="Array"/> index.
/// </summary>
/// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1" />. The <see cref="Array"/> must have zero-based indexing.</param>
/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
public void CopyTo(Trigger[] array, int arrayIndex) { CopyTo(0, array, arrayIndex, Count); }
/// <summary>Determines whether the specified trigger type is contained in this collection.</summary>
/// <param name="triggerType">Type of the trigger.</param>
/// <returns><c>true</c> if the specified trigger type is contained in this collection; otherwise, <c>false</c>.</returns>
public bool ContainsType(Type triggerType) => Find(a => a.GetType() == triggerType) != null;
/// <summary>
/// Copies the elements of the <see cref="TriggerCollection" /> to a <see cref="Trigger" /> array, starting at a particular <see cref="Trigger" /> array index.
/// Copies the elements of the <see cref="ICollection{T}"/> to an <see cref="Array"/>, starting at a particular <see cref="Array"/> index.
/// </summary>
/// <param name="array">
/// The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ICollection{T}"/>. The
/// <see cref="Array"/> must have zero-based indexing.
/// </param>
/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
public void CopyTo(Trigger[] array, int arrayIndex) => CopyTo(0, array, arrayIndex, Count);
/// <summary>
/// Copies the elements of the <see cref="TriggerCollection"/> to a <see cref="Trigger"/> array, starting at a particular <see
/// cref="Trigger"/> array index.
/// </summary>
/// <param name="index">The zero-based index in the source at which copying begins.</param>
/// <param name="array">The <see cref="Trigger" /> array that is the destination of the elements copied from <see cref="TriggerCollection" />. The <see cref="Trigger" /> array must have zero-based indexing.</param>
/// <param name="array">
/// The <see cref="Trigger"/> array that is the destination of the elements copied from <see cref="TriggerCollection"/>. The <see
/// cref="Trigger"/> array must have zero-based indexing.
/// </param>
/// <param name="arrayIndex">The zero-based index in <see cref="Trigger"/> array at which copying begins.</param>
/// <param name="count">The number of elements to copy.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="array" /> is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException"><paramref name="arrayIndex" /> is less than 0.</exception>
/// <exception cref="System.ArgumentException">The number of elements in the source <see cref="TriggerCollection" /> is greater than the available space from <paramref name="arrayIndex" /> to the end of the destination <paramref name="array" />.</exception>
/// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception>
/// <exception cref="ArgumentException">
/// The number of elements in the source <see cref="TriggerCollection"/> is greater than the available space from <paramref
/// name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
/// </exception>
public void CopyTo(int index, Trigger[] array, int arrayIndex, int count)
{
if (array == null)
@ -160,13 +267,11 @@ namespace winPEAS.TaskScheduler
throw new ArgumentOutOfRangeException(nameof(count));
if ((Count - index) > (array.Length - arrayIndex))
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
for (int i = 0; i < count; i++)
for (var i = 0; i < count; i++)
array[arrayIndex + i] = (Trigger)this[index + i].Clone();
}
/// <summary>
/// Releases all resources used by this class.
/// </summary>
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
if (v2Coll != null) Marshal.ReleaseComObject(v2Coll);
@ -175,10 +280,15 @@ namespace winPEAS.TaskScheduler
}
/// <summary>
/// Searches for an <see cref="Trigger"/> that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire collection.
/// Searches for an <see cref="Trigger"/> that matches the conditions defined by the specified predicate, and returns the first
/// occurrence within the entire collection.
/// </summary>
/// <param name="match">The <see cref="Predicate{Trigger}"/> delegate that defines the conditions of the <see cref="Trigger"/> to search for.</param>
/// <returns>The first <see cref="Trigger"/> that matches the conditions defined by the specified predicate, if found; otherwise, <c>null</c>.</returns>
/// <param name="match">
/// The <see cref="Predicate{Trigger}"/> delegate that defines the conditions of the <see cref="Trigger"/> to search for.
/// </param>
/// <returns>
/// The first <see cref="Trigger"/> that matches the conditions defined by the specified predicate, if found; otherwise, <c>null</c>.
/// </returns>
public Trigger Find([NotNull] Predicate<Trigger> match)
{
if (match == null)
@ -189,12 +299,15 @@ namespace winPEAS.TaskScheduler
}
/// <summary>
/// Searches for an <see cref="Trigger"/> 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.
/// Searches for an <see cref="Trigger"/> 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.
/// </summary>
/// <param name="startIndex">The zero-based starting index of the search.</param>
/// <param name="count">The number of elements in the collection to search.</param>
/// <param name="match">The <see cref="Predicate{Trigger}"/> delegate that defines the conditions of the element to search for.</param>
/// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.</returns>
/// <returns>
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.
/// </returns>
public int FindIndexOf(int startIndex, int count, [NotNull] Predicate<Trigger> match)
{
if (startIndex < 0 || startIndex >= Count)
@ -203,29 +316,135 @@ namespace winPEAS.TaskScheduler
throw new ArgumentOutOfRangeException(nameof(count));
if (match == null)
throw new ArgumentNullException(nameof(match));
for (int i = startIndex; i < startIndex + count; i++)
for (var i = startIndex; i < startIndex + count; i++)
if (match(this[i])) return i;
return -1;
}
/// <summary>
/// Searches for an <see cref="Trigger"/> that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the collection.
/// Searches for an <see cref="Trigger"/> that matches the conditions defined by the specified predicate, and returns the zero-based
/// index of the first occurrence within the collection.
/// </summary>
/// <param name="match">The <see cref="Predicate{Trigger}"/> delegate that defines the conditions of the element to search for.</param>
/// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.</returns>
/// <returns>
/// The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.
/// </returns>
public int FindIndexOf([NotNull] Predicate<Trigger> match) => FindIndexOf(0, Count, match);
/// <summary>
/// Gets the collection enumerator for this collection.
/// </summary>
/// <summary>Gets the collection enumerator for this collection.</summary>
/// <returns>The <see cref="IEnumerator{T}"/> for this collection.</returns>
public IEnumerator<Trigger> GetEnumerator()
{
if (v1Task != null)
return new V1TriggerEnumerator(v1Task);
return new ComEnumerator<Trigger, ITrigger>(() => v2Coll.Count, i => v2Coll[i], o => Trigger.CreateTrigger(o, v2Def));
return new ComEnumerator<Trigger, V2Interop.ITrigger>(() => v2Coll.Count, i => v2Coll[i], o => Trigger.CreateTrigger(o, v2Def));
}
/// <summary>Determines the index of a specific item in the <see cref="IList{T}"/>.</summary>
/// <param name="item">The object to locate in the <see cref="IList{T}"/>.</param>
/// <returns>The index of <paramref name="item"/> if found in the list; otherwise, -1.</returns>
public int IndexOf([NotNull] Trigger item) => FindIndexOf(a => a.Equals(item));
/// <summary>Determines the index of a specific item in the <see cref="IList{T}"/>.</summary>
/// <param name="triggerId">The id ( <see cref="Trigger.Id"/>) of the trigger to be retrieved.</param>
/// <returns>The index of <paramref name="triggerId"/> if found in the list; otherwise, -1.</returns>
public int IndexOf([NotNull] string triggerId)
{
if (string.IsNullOrEmpty(triggerId))
throw new ArgumentNullException(triggerId);
return FindIndexOf(a => string.Equals(a.Id, triggerId));
}
/// <summary>Inserts an trigger at the specified index.</summary>
/// <param name="index">The zero-based index at which trigger should be inserted.</param>
/// <param name="trigger">The trigger to insert into the list.</param>
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);
}
/// <summary>Removes the first occurrence of a specific object from the <see cref="ICollection{T}"/>.</summary>
/// <param name="item">The object to remove from the <see cref="ICollection{T}"/>.</param>
/// <returns>
/// true if <paramref name="item"/> was successfully removed from the <see cref="ICollection{T}"/>; otherwise, false. This method
/// also returns false if <paramref name="item"/> is not found in the original <see cref="ICollection{T}"/>.
/// </returns>
public bool Remove([NotNull] Trigger item)
{
var idx = IndexOf(item);
if (idx != -1)
{
try
{
RemoveAt(idx);
return true;
}
catch { }
}
return false;
}
/// <summary>Removes the trigger at a specified index.</summary>
/// <param name="index">Index of trigger to remove.</param>
/// <exception cref="ArgumentOutOfRangeException">Index out of range.</exception>
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));
}
}
/// <summary>Copies the elements of the <see cref="TriggerCollection"/> to a new array.</summary>
/// <returns>An array containing copies of the elements of the <see cref="TriggerCollection"/>.</returns>
public Trigger[] ToArray()
{
var ret = new Trigger[Count];
CopyTo(ret, 0);
return ret;
}
/// <summary>Returns a <see cref="string"/> that represents the triggers in this collection.</summary>
/// <returns>A <see cref="string"/> that represents the triggers in this collection.</returns>
public override string ToString()
{
if (Count == 1)
return this[0].ToString();
if (Count > 1)
return Properties.Resources.MultipleTriggers;
return string.Empty;
}
void ICollection<Trigger>.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)
@ -235,53 +454,13 @@ namespace winPEAS.TaskScheduler
Array.Copy(src, 0, array, index, Count);
}
void ICollection<Trigger>.Add(Trigger item) { Add(item); }
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
int IList.Add(object value)
{
Add((Trigger)value);
return Count - 1;
}
bool IList.Contains(object value) => Contains((Trigger)value);
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 IList.Remove(object value) { Remove((Trigger)value); }
/// <summary>
/// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1" />.
/// </summary>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1" />.</param>
/// <returns>
/// The index of <paramref name="item" /> if found in the list; otherwise, -1.
/// </returns>
public int IndexOf([NotNull] Trigger item) => FindIndexOf(a => a.Equals(item));
/// <summary>
/// Inserts an trigger at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which trigger should be inserted.</param>
/// <param name="trigger">The trigger to insert into the list.</param>
public void Insert(int index, [NotNull] Trigger trigger)
{
if (trigger == null)
throw new ArgumentNullException(nameof(trigger));
if (index >= Count)
throw new ArgumentOutOfRangeException(nameof(index));
Trigger[] pushItems = new Trigger[Count - index];
CopyTo(index, pushItems, 0, Count - index);
for (int j = Count - 1; j >= index; j--)
RemoveAt(j);
Add(trigger);
foreach (Trigger t in pushItems)
Add(t);
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null;
void IList.Insert(int index, object value) => Insert(index, (Trigger)value);
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
@ -318,92 +497,41 @@ namespace winPEAS.TaskScheduler
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);
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
/// <returns>
/// true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </returns>
public bool Remove([NotNull] Trigger item)
{
int idx = IndexOf(item);
if (idx != -1)
{
try
{
RemoveAt(idx);
return true;
}
catch { }
}
return false;
}
/// <summary>
/// Removes the trigger at a specified index.
/// </summary>
/// <param name="index">Index of trigger to remove.</param>
/// <exception cref="ArgumentOutOfRangeException">Index out of range.</exception>
public void RemoveAt(int index)
{
if (index >= Count)
throw new ArgumentOutOfRangeException(nameof(index), index, @"Failed to remove Trigger. Index out of range.");
if (v2Coll != null)
v2Coll.Remove(++index);
else
v1Task.DeleteTrigger((ushort)index); //Remove the trigger from the Task Scheduler
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Returns a <see cref="System.String"/> that represents the triggers in this collection.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents the triggers in this collection.
/// </returns>
public override string ToString()
{
if (Count == 1)
return this[0].ToString();
if (Count > 1)
return winPEAS.Properties.Resources.MultipleTriggers;
return string.Empty;
}
internal void Bind()
{
foreach (Trigger t in this)
foreach (var t in this)
t.SetV1TriggerData();
}
/// <summary>Called when a property has changed to notify any attached elements.</summary>
/// <param name="propertyName">Name of the property.</param>
private void OnNotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private sealed class V1TriggerEnumerator : IEnumerator<Trigger>
{
private short curItem = -1;
private ITask iTask;
private V1Interop.ITask iTask;
internal V1TriggerEnumerator(ITask task) { iTask = task; }
internal V1TriggerEnumerator(V1Interop.ITask task) => iTask = task;
public Trigger Current => Trigger.CreateTrigger(iTask.GetTrigger((ushort)curItem));
object IEnumerator.Current => Current;
/// <summary>
/// Releases all resources used by this class.
/// </summary>
public void Dispose() { iTask = null; }
/// <summary>Releases all resources used by this class.</summary>
public void Dispose() => iTask = null;
public bool MoveNext() => (++curItem < iTask.GetTriggerCount());
public void Reset() { curItem = -1; }
public void Reset() => curItem = -1;
}
}
}

View File

@ -1,6 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using winPEAS.TaskScheduler.Native;
using System.Text;
using System.Threading.Tasks;
using winPEAS.TaskScheduler.TaskEditor.Native;
namespace winPEAS.TaskScheduler
{
@ -66,10 +70,22 @@ namespace winPEAS.TaskScheduler
/// <param name="wid">The <see cref="WindowsIdentity"/>.</param>
internal User(WindowsIdentity wid) { Identity = wid; sid = wid.User; }
/// <summary>Gets the current user.</summary>
/// <value>The current user.</value>
public static User Current => new User(cur);
/// <summary>Gets the identity.</summary>
/// <value>The identity.</value>
public WindowsIdentity Identity { get; private set; }
/// <summary>Gets a value indicating whether this instance is in an administrator role.</summary>
/// <value><c>true</c> if this instance is an admin; otherwise, <c>false</c>.</value>
public bool IsAdmin => Identity != null ? new WindowsPrincipal(Identity).IsInRole(WindowsBuiltInRole.Administrator) : false;
/// <summary>Gets a value indicating whether this instance is the interactive user.</summary>
/// <value><c>true</c> if this instance is the current user; otherwise, <c>false</c>.</value>
public bool IsCurrent => Identity?.User.Equals(cur.User) ?? false;
/// <summary>Gets a value indicating whether this instance is a service account.</summary>
/// <value><c>true</c> if this instance is a service account; otherwise, <c>false</c>.</value>
public bool IsServiceAccount
@ -89,6 +105,10 @@ namespace winPEAS.TaskScheduler
/// <value><c>true</c> if this instance is the SYSTEM account; otherwise, <c>false</c>.</value>
public bool IsSystem => sid != null && sid.IsWellKnown(WellKnownSidType.LocalSystemSid);
/// <summary>Gets the SID string.</summary>
/// <value>The SID string.</value>
public string SidString => sid?.ToString();
/// <summary>Gets the NT name (DOMAIN\username).</summary>
/// <value>The name of the user.</value>
public string Name => Identity?.Name ?? ((NTAccount)sid?.Translate(typeof(NTAccount)))?.Value;

View File

@ -1,11 +1,15 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using winPEAS.TaskScheduler.TaskEditor.Native;
// ReSharper disable InconsistentNaming
// ReSharper disable FieldCanBeMadeReadOnly.Global
namespace winPEAS.TaskScheduler.V1
namespace winPEAS.TaskScheduler.V1Interop
{
#pragma warning disable CS0618 // Type or member is obsolete
#region class HRESULT -- Values peculiar to the task scheduler.
internal class HResult
{
@ -322,8 +326,11 @@ namespace winPEAS.TaskScheduler.V1
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]
@ -386,16 +393,29 @@ namespace winPEAS.TaskScheduler.V1
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);
@ -409,6 +429,7 @@ namespace winPEAS.TaskScheduler.V1
void SetPriority([In] uint Priority);
uint GetPriority();
void SetTaskFlags([In] uint Flags);
uint GetTaskFlags();
void SetMaxRunTime([In] uint MaxRunTimeMS);
uint GetMaxRunTime();
}

View File

@ -0,0 +1,449 @@
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="TaskSchedulerV1Schema"
targetNamespace="http://schemas.microsoft.com/windows/2004/02/mit/task"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"
xmlns:td="http://schemas.microsoft.com/windows/2004/02/mit/task"
elementFormDefault="qualified">
<xs:element name="Task" type="taskType">
<xs:key name="PrincipalKey">
<xs:selector xpath="td:Principals/td:Principal" />
<xs:field xpath="@id" />
</xs:key>
<!-- Principal id in Context attribute should match an id of some principal in Principals section. -->
<xs:keyref name="ContextKeyRef" refer="PrincipalKey">
<xs:selector xpath="td:Actions" />
<xs:field xpath="@Context" />
</xs:keyref>
<!-- All ids must be unique -->
<xs:unique name="UniqueId">
<xs:selector xpath="td:Principals/td:Principal|td:Triggers/td:BootTrigger|td:Triggers/td:IdleTrigger|td:Triggers/td:TimeTrigger|td:Triggers/td:LogonTrigger|td:Triggers/td:CalendarTrigger|td:Actions/td:Exec" />
<xs:field xpath="@id" />
</xs:unique>
</xs:element>
<xs:simpleType name="nonEmptyString">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="pathType">
<xs:restriction base="nonEmptyString">
<xs:maxLength value="260"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="versionType">
<xs:restriction base="xs:string">
<xs:pattern value="\d+(\.\d+){1,3}" />
</xs:restriction>
</xs:simpleType>
<!-- Task -->
<xs:complexType name="taskType">
<xs:all>
<xs:element name="RegistrationInfo" type="registrationInfoType" minOccurs="0" />
<xs:element name="Triggers" type="triggersType" minOccurs="0" />
<xs:element name="Settings" type="settingsType" minOccurs="0" />
<xs:element name="Data" type="dataType" minOccurs="0" />
<xs:element name="Principals" type="principalsType" minOccurs="0" />
<xs:element name="Actions" type="actionsType" />
</xs:all>
<xs:attribute name="version" use="optional" fixed="1.1" type="versionType" />
</xs:complexType>
<!-- RegistrationInfo -->
<xs:complexType name="registrationInfoType">
<xs:all>
<xs:element name="URI" type="xs:anyURI" minOccurs="0" />
<xs:element name="SecurityDescriptor" type="xs:string" minOccurs="0" />
<xs:element name="Source" type="xs:string" minOccurs="0" />
<xs:element name="Date" type="xs:dateTime" minOccurs="0" />
<xs:element name="Author" type="xs:string" minOccurs="0" />
<xs:element name="Version" type="xs:string" minOccurs="0" />
<xs:element name="Description" type="xs:string" minOccurs="0" />
<xs:element name="Documentation" type="xs:string" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- Triggers -->
<xs:complexType name="triggersType">
<xs:group ref="triggerGroup" minOccurs="0" maxOccurs="48"/>
</xs:complexType>
<xs:group name="triggerGroup">
<xs:choice>
<xs:element name="BootTrigger" type="bootTriggerType" minOccurs="0" />
<xs:element name="IdleTrigger" type="idleTriggerType" minOccurs="0" />
<xs:element name="TimeTrigger" type="timeTriggerType" minOccurs="0" />
<xs:element name="LogonTrigger" type="logonTriggerType" minOccurs="0" />
<xs:element name="CalendarTrigger" type="calendarTriggerType" minOccurs="0" />
</xs:choice>
</xs:group>
<!-- Base type for all triggers -->
<xs:complexType name="triggerBaseType" abstract="true">
<xs:sequence>
<xs:element name="Enabled" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="StartBoundary" type="xs:dateTime" minOccurs="0" />
<xs:element name="EndBoundary" type="xs:dateTime" minOccurs="0" />
<xs:element name="Repetition" type="repetitionType" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<!-- Repetition -->
<xs:complexType name="repetitionType">
<xs:all>
<xs:element name="Interval">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
<xs:maxInclusive value="P31D"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Duration" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="StopAtDurationEnd" type="xs:boolean" default="false" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- BootTrigger -->
<xs:complexType name="bootTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType" />
</xs:complexContent>
</xs:complexType>
<!-- IdleTrigger -->
<xs:complexType name="idleTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType" />
</xs:complexContent>
</xs:complexType>
<!-- TimeTrigger -->
<xs:complexType name="timeTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType" />
</xs:complexContent>
</xs:complexType>
<!-- LogonTrigger -->
<xs:complexType name="logonTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType" />
</xs:complexContent>
</xs:complexType>
<!-- CalendarTrigger -->
<xs:complexType name="calendarTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:choice>
<xs:element name="ScheduleByDay" type="dailyScheduleType" />
<xs:element name="ScheduleByWeek" type="weeklyScheduleType" />
<xs:element name="ScheduleByMonth" type="monthlyScheduleType" />
<xs:element name="ScheduleByMonthDayOfWeek" type="monthlyDayOfWeekScheduleType" />
</xs:choice>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- DailySchedule -->
<xs:complexType name="dailyScheduleType">
<xs:all>
<xs:element name="DaysInterval" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:unsignedInt">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="365"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:all>
</xs:complexType>
<!-- WeeklySchedule -->
<xs:complexType name="weeklyScheduleType">
<xs:all>
<xs:element name="WeeksInterval" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="52"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="DaysOfWeek" type="daysOfWeekType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- MonthlySchedule -->
<xs:complexType name="monthlyScheduleType">
<xs:all>
<xs:element name="DaysOfMonth" type="daysOfMonthType" minOccurs="0" />
<xs:element name="Months" type="monthsType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- MonthlyDayOfWeekSchedule -->
<xs:complexType name="monthlyDayOfWeekScheduleType">
<xs:all>
<xs:element name="Weeks" type="weeksType" minOccurs="0" />
<xs:element name="DaysOfWeek" type="daysOfWeekType" />
<xs:element name="Months" type="monthsType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- DaysOfWeek -->
<xs:complexType name="daysOfWeekType">
<xs:all>
<xs:element name="Monday" fixed="" minOccurs="0" />
<xs:element name="Tuesday" fixed="" minOccurs="0" />
<xs:element name="Wednesday" fixed="" minOccurs="0" />
<xs:element name="Thursday" fixed="" minOccurs="0" />
<xs:element name="Friday" fixed="" minOccurs="0" />
<xs:element name="Saturday" fixed="" minOccurs="0" />
<xs:element name="Sunday" fixed="" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- Months -->
<xs:complexType name="monthsType">
<xs:all>
<xs:element name="January" fixed="" minOccurs="0" />
<xs:element name="February" fixed="" minOccurs="0" />
<xs:element name="March" fixed="" minOccurs="0" />
<xs:element name="April" fixed="" minOccurs="0" />
<xs:element name="May" fixed="" minOccurs="0" />
<xs:element name="June" fixed="" minOccurs="0" />
<xs:element name="July" fixed="" minOccurs="0" />
<xs:element name="August" fixed="" minOccurs="0" />
<xs:element name="September" fixed="" minOccurs="0" />
<xs:element name="October" fixed="" minOccurs="0" />
<xs:element name="November" fixed="" minOccurs="0" />
<xs:element name="December" fixed="" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- DaysOfMonth -->
<xs:complexType name="daysOfMonthType">
<xs:sequence>
<xs:element name="Day" type="dayOfMonthType" minOccurs="0" maxOccurs="32" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="dayOfMonthType">
<xs:restriction base="xs:string">
<xs:pattern value="[1-9]|[1-2][0-9]|3[0-1]|Last" />
</xs:restriction>
</xs:simpleType>
<!-- Weeks -->
<xs:complexType name="weeksType">
<xs:sequence>
<xs:element name="Week" type="weekType" minOccurs="0" maxOccurs="5" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="weekType">
<xs:restriction base="xs:string">
<xs:pattern value="[1-4]|Last" />
</xs:restriction>
</xs:simpleType>
<!-- Settings -->
<xs:complexType name="settingsType">
<xs:all>
<xs:element name="DisallowStartIfOnBatteries" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="StopIfGoingOnBatteries" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="RunOnlyIfNetworkAvailable" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="WakeToRun" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="Enabled" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="Hidden" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="DeleteExpiredTaskAfter" type="xs:duration" default="PT0S" minOccurs="0" />
<xs:element name="IdleSettings" type="idleSettingsType" minOccurs="0" />
<xs:element name="ExecutionTimeLimit" type="xs:duration" default="PT72H" minOccurs="0" />
<xs:element name="Priority" type="priorityType" default="7" minOccurs="0" />
<xs:element name="RunOnlyIfIdle" type="xs:boolean" default="false" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- Lower number means higher priority. -->
<xs:simpleType name="priorityType">
<xs:restriction base="xs:byte">
<xs:minInclusive value="0" fixed="true" />
<xs:maxInclusive value="10" fixed="true" />
</xs:restriction>
</xs:simpleType>
<!-- IdleSettings -->
<xs:complexType name="idleSettingsType">
<xs:all>
<xs:element name="Duration" default="PT10M" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="WaitTimeout" default="PT1H" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="StopOnIdleEnd" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="RestartOnIdle" type="xs:boolean" default="false" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- Data -->
<xs:complexType name="dataType">
<xs:sequence>
<xs:any />
</xs:sequence>
</xs:complexType>
<!-- Principals -->
<xs:complexType name="principalsType">
<xs:sequence>
<xs:element name="Principal" type="principalType" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<!-- Principal -->
<xs:complexType name="principalType">
<xs:all>
<xs:element name="UserId" type="nonEmptyString" minOccurs="0" />
<xs:element name="LogonType" type="logonType" minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>
<xs:simpleType name="logonType">
<xs:restriction base="xs:string">
<xs:enumeration value="Password" />
<xs:enumeration value="InteractiveToken" />
<xs:enumeration value="InteractiveTokenOrPassword" />
</xs:restriction>
</xs:simpleType>
<!-- Actions -->
<xs:complexType name="actionsType">
<xs:sequence>
<xs:group ref="actionGroup" maxOccurs="1" />
</xs:sequence>
<xs:attribute name="Context" type="xs:IDREF" use="optional" />
</xs:complexType>
<xs:group name="actionGroup">
<xs:choice>
<xs:element name="Exec" type="execType" />
<xs:element name="ComHandler" type="comHandlerType" />
<xs:element name="SendEmail" type="sendEmailType" />
<xs:element name="ShowMessage" type="showMessageType" />
</xs:choice>
</xs:group>
<!-- Base type for actions. -->
<xs:complexType name="actionBaseType" abstract="true">
<xs:attribute name="id" type="xs:ID" use="optional" />
</xs:complexType>
<!-- Exec -->
<xs:complexType name="execType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="Command" type="pathType"/>
<xs:element name="Arguments" type="xs:string" minOccurs="0"/>
<xs:element name="WorkingDirectory" type="pathType" minOccurs="0"/>
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- ComHandler -->
<xs:complexType name="comHandlerType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="ClassId" type="guidType" />
<xs:element name="Data" type="dataType" minOccurs="0" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="guidType">
<xs:restriction base="xs:string">
<!-- GUID should be in a form: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} where x is a hexadecimal digit. -->
<xs:pattern value="[\{]?([0-9a-fA-F]){8}(\-[0-9a-fA-F]{4}){3}\-[0-9a-fA-F]{12}[\}]?" />
</xs:restriction>
</xs:simpleType>
<!-- SendEmail -->
<xs:complexType name="sendEmailType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="Server" type="nonEmptyString" />
<xs:element name="Subject" type="xs:string" minOccurs="0" />
<xs:element name="To" type="xs:string" minOccurs="0" />
<xs:element name="Cc" type="xs:string" minOccurs="0" />
<xs:element name="Bcc" type="xs:string" minOccurs="0" />
<xs:element name="ReplyTo" type="xs:string" minOccurs="0" />
<xs:element name="From" type="xs:string" minOccurs="0" />
<xs:element name="HeaderFields" type="headerFieldsType" minOccurs="0" />
<xs:element name="Body" type="xs:string" minOccurs="0" />
<xs:element name="Attachments" type="attachmentsType" minOccurs="0" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="headerFieldsType">
<xs:sequence>
<xs:element name="HeaderField" type="headerFieldType" minOccurs="0" maxOccurs="32"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="headerFieldType">
<xs:all>
<xs:element name="Name" type="nonEmptyString" />
<xs:element name="Value" type="xs:string" />
</xs:all>
</xs:complexType>
<xs:complexType name="attachmentsType">
<xs:sequence>
<xs:element name="File" type="nonEmptyString" minOccurs="0" maxOccurs="8"/>
</xs:sequence>
</xs:complexType>
<!-- ShowMessage -->
<xs:complexType name="showMessageType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="Title" type="nonEmptyString" />
<xs:element name="Body" type="nonEmptyString" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -1,15 +1,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using winPEAS.TaskScheduler.Native;
using System.Text;
using System.Threading.Tasks;
using winPEAS.TaskScheduler.TaskEditor.Native;
using winPEAS.TaskScheduler.V1Interop;
namespace winPEAS.TaskScheduler.V2
namespace winPEAS.TaskScheduler.V2Interop
{
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; }
}
@ -19,6 +32,9 @@ namespace winPEAS.TaskScheduler.V2
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();
@ -496,6 +512,19 @@ namespace winPEAS.TaskScheduler.V2
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
{
@ -551,4 +580,3 @@ namespace winPEAS.TaskScheduler.V2
string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
}

View File

@ -0,0 +1,691 @@
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema
targetNamespace="http://schemas.microsoft.com/windows/2004/02/mit/task"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"
xmlns:td="http://schemas.microsoft.com/windows/2004/02/mit/task"
elementFormDefault="qualified">
<xs:element name="Task" type="taskType">
<xs:key name="PrincipalKey">
<xs:selector xpath="td:Principals/td:Principal" />
<xs:field xpath="@id" />
</xs:key>
<!-- Principal id in Context attribute should match an id of some principal in Principals section. -->
<xs:keyref name="ContextKeyRef" refer="PrincipalKey">
<xs:selector xpath="td:Actions" />
<xs:field xpath="@Context" />
</xs:keyref>
<!-- All ids must be unique -->
<xs:unique name="UniqueId">
<xs:selector xpath="td:Principals/td:Principal|td:Triggers/td:BootTrigger|td:Triggers/td:RegistrationTrigger|td:Triggers/td:IdleTrigger|td:Triggers/td:TimeTrigger|td:Triggers/td:EventTrigger|td:Triggers/td:LogonTrigger|td:Triggers/td:SessionStateChangeTrigger|td:Triggers/td:CalendarTrigger|td:Actions/td:Exec|td:Actions/td:ComHandler|td:Actions/td:SendEmail" />
<xs:field xpath="@id" />
</xs:unique>
</xs:element>
<xs:simpleType name="nonEmptyString">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="pathType">
<xs:restriction base="nonEmptyString">
<xs:maxLength value="260"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="versionType">
<xs:restriction base="xs:string">
<xs:pattern value="\d+(\.\d+){1,3}" />
</xs:restriction>
</xs:simpleType>
<!-- Task -->
<xs:complexType name="taskType">
<xs:all>
<xs:element name="RegistrationInfo" type="registrationInfoType" minOccurs="0" />
<xs:element name="Triggers" type="triggersType" minOccurs="0" />
<xs:element name="Settings" type="settingsType" minOccurs="0" />
<xs:element name="Data" type="dataType" minOccurs="0" />
<xs:element name="Principals" type="principalsType" minOccurs="0" />
<xs:element name="Actions" type="actionsType" />
</xs:all>
<xs:attribute name="version" use="optional" type="versionType" />
</xs:complexType>
<!-- RegistrationInfo -->
<xs:complexType name="registrationInfoType">
<xs:all>
<xs:element name="URI" type="xs:anyURI" minOccurs="0" />
<xs:element name="SecurityDescriptor" type="xs:string" minOccurs="0" />
<xs:element name="Source" type="xs:string" minOccurs="0" />
<xs:element name="Date" type="xs:dateTime" minOccurs="0" />
<xs:element name="Author" type="xs:string" minOccurs="0" />
<xs:element name="Version" type="xs:string" minOccurs="0" />
<xs:element name="Description" type="xs:string" minOccurs="0" />
<xs:element name="Documentation" type="xs:string" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- Triggers -->
<xs:complexType name="triggersType">
<xs:group ref="triggerGroup" minOccurs="0" maxOccurs="48"/>
</xs:complexType>
<xs:group name="triggerGroup">
<xs:choice>
<xs:element name="BootTrigger" type="bootTriggerType" minOccurs="0" />
<xs:element name="RegistrationTrigger" type="registrationTriggerType" minOccurs="0" />
<xs:element name="IdleTrigger" type="idleTriggerType" minOccurs="0" />
<xs:element name="TimeTrigger" type="timeTriggerType" minOccurs="0" />
<xs:element name="EventTrigger" type="eventTriggerType" minOccurs="0" />
<xs:element name="LogonTrigger" type="logonTriggerType" minOccurs="0" />
<xs:element name="SessionStateChangeTrigger" type="sessionStateChangeTriggerType" minOccurs="0" />
<xs:element name="CalendarTrigger" type="calendarTriggerType" minOccurs="0" />
<xs:element name="WnfStateChangeTrigger" type="customTriggerType" minOccurs="0" />
</xs:choice>
</xs:group>
<!-- Base type for all triggers -->
<xs:complexType name="triggerBaseType" abstract="true">
<xs:sequence>
<xs:element name="Repetition" type="repetitionType" minOccurs="0" />
<xs:element name="StartBoundary" type="xs:dateTime" minOccurs="0" />
<xs:element name="EndBoundary" type="xs:dateTime" minOccurs="0" />
<xs:element name="ExecutionTimeLimit" type="xs:duration" default="PT72H" minOccurs="0" />
<xs:element name="Enabled" type="xs:boolean" default="true" minOccurs="0" />
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="optional" />
</xs:complexType>
<!-- Repetition -->
<xs:complexType name="repetitionType">
<xs:all>
<xs:element name="Interval">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
<xs:maxInclusive value="P31D"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Duration" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="StopAtDurationEnd" type="xs:boolean" default="false" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- BootTrigger -->
<xs:complexType name="bootTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="Delay" type="xs:duration" default="PT0M" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- CustomTrigger -->
<xs:complexType name="customTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:any minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- RegistrationTrigger -->
<xs:complexType name="registrationTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="Delay" type="xs:duration" default="PT0M" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- IdleTrigger -->
<xs:complexType name="idleTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType" />
</xs:complexContent>
</xs:complexType>
<!-- TimeTrigger -->
<xs:complexType name="timeTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="RandomDelay" type="xs:duration" default="PT0M" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!--valueQueries-->
<xs:complexType name="namedValues">
<xs:sequence>
<xs:element name="Value" type="namedValue" minOccurs="1" maxOccurs="32"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="namedValue">
<xs:simpleContent>
<xs:extension base="nonEmptyString">
<xs:attribute name="name" type="nonEmptyString" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- EventTrigger -->
<xs:complexType name="eventTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="Subscription" type="nonEmptyString" />
<xs:element name="Delay" type="xs:duration" default="PT0M" minOccurs="0" />
<xs:element name="PeriodOfOccurrence" type="xs:duration" default="PT0M" minOccurs="0" />
<xs:element name="NumberOfOccurrences" default="1" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="32"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="MatchingElement" type="nonEmptyString" minOccurs="0" />
<xs:element name="ValueQueries" type="namedValues" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- LogonTrigger -->
<xs:complexType name="logonTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="UserId" type="nonEmptyString" minOccurs="0" maxOccurs="1" />
<xs:element name="Delay" type="xs:duration" default="PT0M" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- SessionStateChangeTrigger -->
<xs:simpleType name="sessionStateChangeType">
<xs:restriction base="xs:string">
<xs:enumeration value="ConsoleConnect" />
<xs:enumeration value="ConsoleDisconnect" />
<xs:enumeration value="RemoteConnect" />
<xs:enumeration value="RemoteDisconnect" />
<xs:enumeration value="SessionLock" />
<xs:enumeration value="SessionUnlock" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="sessionStateChangeTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="StateChange" type="sessionStateChangeType" minOccurs="1" maxOccurs="1"/>
<xs:element name="UserId" type="nonEmptyString" minOccurs="0" maxOccurs="1"/>
<xs:element name="Delay" type="xs:duration" default="PT0M" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- CalendarTrigger -->
<xs:complexType name="calendarTriggerType">
<xs:complexContent>
<xs:extension base="triggerBaseType">
<xs:sequence>
<xs:element name="RandomDelay" type="xs:duration" default="PT0M" minOccurs="0" />
<xs:choice>
<xs:element name="ScheduleByDay" type="dailyScheduleType" />
<xs:element name="ScheduleByWeek" type="weeklyScheduleType" />
<xs:element name="ScheduleByMonth" type="monthlyScheduleType" />
<xs:element name="ScheduleByMonthDayOfWeek" type="monthlyDayOfWeekScheduleType" />
</xs:choice>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- DailySchedule -->
<xs:complexType name="dailyScheduleType">
<xs:all>
<xs:element name="DaysInterval" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:unsignedInt">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="365"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:all>
</xs:complexType>
<!-- WeeklySchedule -->
<xs:complexType name="weeklyScheduleType">
<xs:all>
<xs:element name="WeeksInterval" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="52"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="DaysOfWeek" type="daysOfWeekType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- MonthlySchedule -->
<xs:complexType name="monthlyScheduleType">
<xs:all>
<xs:element name="DaysOfMonth" type="daysOfMonthType" minOccurs="0" />
<xs:element name="Months" type="monthsType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- MonthlyDayOfWeekSchedule -->
<xs:complexType name="monthlyDayOfWeekScheduleType">
<xs:all>
<xs:element name="Weeks" type="weeksType" minOccurs="0" />
<xs:element name="DaysOfWeek" type="daysOfWeekType" />
<xs:element name="Months" type="monthsType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- DaysOfWeek -->
<xs:complexType name="daysOfWeekType">
<xs:all>
<xs:element name="Monday" fixed="" minOccurs="0" />
<xs:element name="Tuesday" fixed="" minOccurs="0" />
<xs:element name="Wednesday" fixed="" minOccurs="0" />
<xs:element name="Thursday" fixed="" minOccurs="0" />
<xs:element name="Friday" fixed="" minOccurs="0" />
<xs:element name="Saturday" fixed="" minOccurs="0" />
<xs:element name="Sunday" fixed="" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- Months -->
<xs:complexType name="monthsType">
<xs:all>
<xs:element name="January" fixed="" minOccurs="0" />
<xs:element name="February" fixed="" minOccurs="0" />
<xs:element name="March" fixed="" minOccurs="0" />
<xs:element name="April" fixed="" minOccurs="0" />
<xs:element name="May" fixed="" minOccurs="0" />
<xs:element name="June" fixed="" minOccurs="0" />
<xs:element name="July" fixed="" minOccurs="0" />
<xs:element name="August" fixed="" minOccurs="0" />
<xs:element name="September" fixed="" minOccurs="0" />
<xs:element name="October" fixed="" minOccurs="0" />
<xs:element name="November" fixed="" minOccurs="0" />
<xs:element name="December" fixed="" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- DaysOfMonth -->
<xs:complexType name="daysOfMonthType">
<xs:sequence>
<xs:element name="Day" type="dayOfMonthType" minOccurs="0" maxOccurs="32" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="dayOfMonthType">
<xs:restriction base="xs:string">
<xs:pattern value="[1-9]|[1-2][0-9]|3[0-1]|Last" />
</xs:restriction>
</xs:simpleType>
<!-- Weeks -->
<xs:complexType name="weeksType">
<xs:sequence>
<xs:element name="Week" type="weekType" minOccurs="0" maxOccurs="5" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="weekType">
<xs:restriction base="xs:string">
<xs:pattern value="[1-4]|Last" />
</xs:restriction>
</xs:simpleType>
<!-- Settings -->
<xs:complexType name="settingsType">
<xs:all>
<xs:element name="AllowStartOnDemand" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="RestartOnFailure" type="restartType" minOccurs="0" />
<xs:element name="MultipleInstancesPolicy" type="multipleInstancesPolicyType" default="IgnoreNew" minOccurs="0" />
<xs:element name="DisallowStartIfOnBatteries" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="StopIfGoingOnBatteries" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="AllowHardTerminate" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="StartWhenAvailable" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="NetworkProfileName" type="xs:string" minOccurs="0" />
<xs:element name="RunOnlyIfNetworkAvailable" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="WakeToRun" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="Enabled" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="Hidden" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="DeleteExpiredTaskAfter" type="xs:duration" default="PT0S" minOccurs="0" />
<xs:element name="IdleSettings" type="idleSettingsType" minOccurs="0" />
<xs:element name="NetworkSettings" type="networkSettingsType" minOccurs="0" />
<xs:element name="ExecutionTimeLimit" type="xs:duration" default="PT72H" minOccurs="0" />
<xs:element name="Priority" type="priorityType" default="7" minOccurs="0" />
<xs:element name="RunOnlyIfIdle" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="UseUnifiedSchedulingEngine" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="Volatile" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="DisallowStartOnRemoteAppSession" type="xs:boolean" default="false" minOccurs="0" />
<xs:element name="MaintenanceSettings" type="maintenanceSettingsType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- MaintenanceSettings -->
<xs:complexType name="maintenanceSettingsType">
<xs:all>
<xs:element name="Period" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="P1D" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Deadline" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="P1D" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Exclusive" type="xs:boolean" maxOccurs="1" minOccurs="1" />
</xs:all>
</xs:complexType>
<!-- MultipleInstancesPolicy -->
<xs:simpleType name="multipleInstancesPolicyType">
<xs:restriction base="xs:string">
<xs:enumeration value="Parallel" />
<xs:enumeration value="Queue" />
<xs:enumeration value="IgnoreNew" />
<xs:enumeration value="StopExisting" />
</xs:restriction>
</xs:simpleType>
<!-- Lower number means higher priority. -->
<xs:simpleType name="priorityType">
<xs:restriction base="xs:byte">
<xs:minInclusive value="0" fixed="true" />
<xs:maxInclusive value="10" fixed="true" />
</xs:restriction>
</xs:simpleType>
<!-- IdleSettings -->
<xs:complexType name="idleSettingsType">
<xs:all>
<xs:element name="Duration" default="PT10M" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="WaitTimeout" default="PT1H" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="StopOnIdleEnd" type="xs:boolean" default="true" minOccurs="0" />
<xs:element name="RestartOnIdle" type="xs:boolean" default="false" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- NetworkSettings -->
<xs:complexType name="networkSettingsType">
<xs:all>
<xs:element name="Name" type="nonEmptyString" minOccurs="0" />
<xs:element name="Id" type="guidType" minOccurs="0" />
</xs:all>
</xs:complexType>
<!-- RestartOnFailure -->
<xs:complexType name="restartType">
<xs:all>
<xs:element name="Interval">
<xs:simpleType>
<xs:restriction base="xs:duration">
<xs:minInclusive value="PT1M"/>
<xs:maxInclusive value="P31D"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Count">
<xs:simpleType>
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:all>
</xs:complexType>
<!-- Data -->
<xs:simpleType name="dataType">
<xs:restriction base="xs:string" />
</xs:simpleType>
<!-- Principals -->
<xs:complexType name="principalsType">
<xs:sequence>
<xs:element name="Principal" type="principalType" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<!-- Principal -->
<xs:complexType name="principalType">
<xs:all>
<xs:element name="UserId" type="nonEmptyString" minOccurs="0" />
<xs:element name="LogonType" type="logonType" minOccurs="0" maxOccurs="1"/>
<xs:element name="GroupId" type="nonEmptyString" minOccurs="0" />
<xs:element name="DisplayName" type="xs:string" minOccurs="0" />
<xs:element name="RunLevel" type="runLevelType" minOccurs="0" maxOccurs="1"/>
<xs:element name="ProcessTokenSidType" type="processTokenSidType" minOccurs="0" maxOccurs="1"/>
<xs:element name="RequiredPrivileges" type="requiredPrivilegesType" minOccurs="0" />
</xs:all>
<xs:attribute name="id" type="xs:ID" use="optional" />
</xs:complexType>
<xs:simpleType name="logonType">
<xs:restriction base="xs:string">
<xs:enumeration value="S4U" />
<xs:enumeration value="Password" />
<xs:enumeration value="InteractiveToken" />
<!-- for backward compatibility -->
<xs:enumeration value="InteractiveTokenOrPassword" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="runLevelType">
<xs:restriction base="xs:string">
<xs:enumeration value="LeastPrivilege" />
<xs:enumeration value="HighestAvailable" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="processTokenSidType">
<xs:restriction base="xs:string">
<xs:enumeration value="None" />
<xs:enumeration value="Unrestricted" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="requiredPrivilegesType">
<xs:sequence>
<xs:element name="Privilege" type="privilegeType" minOccurs="1" maxOccurs="64"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="privilegeType">
<xs:restriction base="xs:string">
<xs:enumeration value="SeCreateTokenPrivilege" />
<xs:enumeration value="SeAssignPrimaryTokenPrivilege" />
<xs:enumeration value="SeLockMemoryPrivilege" />
<xs:enumeration value="SeIncreaseQuotaPrivilege" />
<xs:enumeration value="SeUnsolicitedInputPrivilege" />
<xs:enumeration value="SeMachineAccountPrivilege" />
<xs:enumeration value="SeTcbPrivilege" />
<xs:enumeration value="SeSecurityPrivilege" />
<xs:enumeration value="SeTakeOwnershipPrivilege" />
<xs:enumeration value="SeLoadDriverPrivilege" />
<xs:enumeration value="SeSystemProfilePrivilege" />
<xs:enumeration value="SeSystemtimePrivilege" />
<xs:enumeration value="SeProfileSingleProcessPrivilege" />
<xs:enumeration value="SeIncreaseBasePriorityPrivilege" />
<xs:enumeration value="SeCreatePagefilePrivilege" />
<xs:enumeration value="SeCreatePermanentPrivilege" />
<xs:enumeration value="SeBackupPrivilege" />
<xs:enumeration value="SeRestorePrivilege" />
<xs:enumeration value="SeShutdownPrivilege" />
<xs:enumeration value="SeDebugPrivilege" />
<xs:enumeration value="SeAuditPrivilege" />
<xs:enumeration value="SeSystemEnvironmentPrivilege" />
<xs:enumeration value="SeChangeNotifyPrivilege" />
<xs:enumeration value="SeRemoteShutdownPrivilege" />
<xs:enumeration value="SeUndockPrivilege" />
<xs:enumeration value="SeSyncAgentPrivilege" />
<xs:enumeration value="SeEnableDelegationPrivilege" />
<xs:enumeration value="SeManageVolumePrivilege" />
<xs:enumeration value="SeImpersonatePrivilege" />
<xs:enumeration value="SeCreateGlobalPrivilege" />
<xs:enumeration value="SeTrustedCredManAccessPrivilege" />
<xs:enumeration value="SeRelabelPrivilege" />
<xs:enumeration value="SeIncreaseWorkingSetPrivilege" />
<xs:enumeration value="SeTimeZonePrivilege" />
<xs:enumeration value="SeCreateSymbolicLinkPrivilege" />
</xs:restriction>
</xs:simpleType>
<!-- Actions -->
<xs:complexType name="actionsType">
<xs:sequence>
<xs:group ref="actionGroup" maxOccurs="32" />
</xs:sequence>
<xs:attribute name="Context" type="xs:IDREF" use="optional" />
</xs:complexType>
<xs:group name="actionGroup">
<xs:choice>
<xs:element name="Exec" type="execType" />
<xs:element name="ComHandler" type="comHandlerType" />
<xs:element name="SendEmail" type="sendEmailType" />
<xs:element name="ShowMessage" type="showMessageType" />
</xs:choice>
</xs:group>
<!-- Base type for actions. -->
<xs:complexType name="actionBaseType" abstract="true">
<xs:attribute name="id" type="xs:ID" use="optional" />
</xs:complexType>
<!-- Exec -->
<xs:complexType name="execType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="Command" type="pathType"/>
<xs:element name="Arguments" type="xs:string" minOccurs="0"/>
<xs:element name="WorkingDirectory" type="pathType" minOccurs="0"/>
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- ComHandler -->
<xs:complexType name="comHandlerType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="ClassId" type="guidType" />
<xs:element name="Data" type="dataType" minOccurs="0" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="guidType">
<xs:restriction base="xs:string">
<!-- GUID should be in a form: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} where x is a hexadecimal digit. -->
<xs:pattern value="[\{]?([0-9a-fA-F]){8}(\-[0-9a-fA-F]{4}){3}\-[0-9a-fA-F]{12}[\}]?" />
</xs:restriction>
</xs:simpleType>
<!-- SendEmail -->
<xs:complexType name="sendEmailType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="Server" type="nonEmptyString" />
<xs:element name="Subject" type="xs:string" minOccurs="0" />
<xs:element name="To" type="xs:string" minOccurs="0" />
<xs:element name="Cc" type="xs:string" minOccurs="0" />
<xs:element name="Bcc" type="xs:string" minOccurs="0" />
<xs:element name="ReplyTo" type="xs:string" minOccurs="0" />
<xs:element name="From" type="xs:string" minOccurs="0" />
<xs:element name="HeaderFields" type="headerFieldsType" minOccurs="0" />
<xs:element name="Body" type="xs:string" minOccurs="0" />
<xs:element name="Attachments" type="attachmentsType" minOccurs="0" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="headerFieldsType">
<xs:sequence>
<xs:element name="HeaderField" type="headerFieldType" minOccurs="0" maxOccurs="32"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="headerFieldType">
<xs:all>
<xs:element name="Name" type="nonEmptyString" />
<xs:element name="Value" type="xs:string" />
</xs:all>
</xs:complexType>
<xs:complexType name="attachmentsType">
<xs:sequence>
<xs:element name="File" type="nonEmptyString" minOccurs="0" maxOccurs="8"/>
</xs:sequence>
</xs:complexType>
<!-- ShowMessage -->
<xs:complexType name="showMessageType">
<xs:complexContent>
<xs:extension base="actionBaseType">
<xs:all>
<xs:element name="Title" type="nonEmptyString" />
<xs:element name="Body" type="nonEmptyString" />
</xs:all>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -1,4 +1,9 @@
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace winPEAS.TaskScheduler
{

View File

@ -1,9 +1,14 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using winPEAS.TaskScheduler.TaskEditor.Native;
namespace winPEAS.TaskScheduler.Native
namespace winPEAS.TaskScheduler
{
/// <summary>
/// Impersonation of a user. Allows to execute code under another

View File

@ -1,8 +1,10 @@
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;

View File

@ -166,6 +166,38 @@
<Compile Include="Info\UserInfo\Token\Structs.cs" />
<Compile Include="Info\UserInfo\Token\Token.cs" />
<Compile Include="Info\UserInfo\User.cs" />
<Compile Include="TaskScheduler\AccessControlExtension.cs" />
<Compile Include="TaskScheduler\Action.cs" />
<Compile Include="TaskScheduler\ActionCollection.cs" />
<Compile Include="TaskScheduler\CultureSwitcher.cs" />
<Compile Include="TaskScheduler\EnumGlobalizer.cs" />
<Compile Include="TaskScheduler\EnumUtil.cs" />
<Compile Include="TaskScheduler\JetBrains.Annotations.cs" />
<Compile Include="TaskScheduler\NamedValueCollection.cs" />
<Compile Include="TaskScheduler\NotV1SupportedException.cs" />
<Compile Include="TaskScheduler\ReflectionHelper.cs" />
<Compile Include="TaskScheduler\Task.cs" />
<Compile Include="TaskScheduler\TaskCollection.cs" />
<Compile Include="TaskScheduler\TaskEditor\Native\ADVAPI32.cs" />
<Compile Include="TaskScheduler\TaskEditor\Native\InteropUtil.cs" />
<Compile Include="TaskScheduler\TaskEditor\Native\KERNEL32.cs" />
<Compile Include="TaskScheduler\TaskEditor\Native\NetServerEnum.cs" />
<Compile Include="TaskScheduler\TaskEditor\Native\NTDSAPI.cs" />
<Compile Include="TaskScheduler\TaskEditor\Native\SYSTEMTIME.cs" />
<Compile Include="TaskScheduler\TaskEvent.cs" />
<Compile Include="TaskScheduler\TaskFolder.cs" />
<Compile Include="TaskScheduler\TaskFolderCollection.cs" />
<Compile Include="TaskScheduler\TaskHandlerInterfaces.cs" />
<Compile Include="TaskScheduler\TaskSecurity.cs" />
<Compile Include="TaskScheduler\TaskService.cs" />
<Compile Include="TaskScheduler\Trigger.cs" />
<Compile Include="TaskScheduler\TriggerCollection.cs" />
<Compile Include="TaskScheduler\User.cs" />
<Compile Include="TaskScheduler\V1\TaskSchedulerV1Interop.cs" />
<Compile Include="TaskScheduler\V2\TaskSchedulerV2Interop.cs" />
<Compile Include="TaskScheduler\Wildcard.cs" />
<Compile Include="TaskScheduler\WindowsImpersonatedIdentity.cs" />
<Compile Include="TaskScheduler\XmlSerializationHelper.cs" />
<Compile Include="Wifi\NativeWifiApi\Enums.cs" />
<Compile Include="Wifi\NativeWifiApi\Structs.cs" />
<Compile Include="Info\NetworkInfo\Firewall.cs" />
@ -182,41 +214,6 @@
</Compile>
<Compile Include="Info\ServicesInfo\ServicesInfoHelper.cs" />
<Compile Include="Info\SystemInfo\SystemInfo.cs" />
<Compile Include="TaskScheduler\Action.cs" />
<Compile Include="TaskScheduler\ActionCollection.cs" />
<Compile Include="TaskScheduler\CultureSwitcher.cs" />
<Compile Include="TaskScheduler\EnumGlobalizer.cs" />
<Compile Include="TaskScheduler\JetBrains.Annotations.cs" />
<Compile Include="TaskScheduler\NamedValueCollection.cs" />
<Compile Include="TaskScheduler\Native\AccessControlExtension.cs" />
<Compile Include="TaskScheduler\Native\ADVAPI32.cs" />
<Compile Include="TaskScheduler\Native\EnumUtil.cs" />
<Compile Include="TaskScheduler\Native\InteropUtil.cs" />
<Compile Include="TaskScheduler\Native\KERNEL32.cs" />
<Compile Include="TaskScheduler\Native\NetServerEnum.cs" />
<Compile Include="TaskScheduler\Native\NTDSAPI.cs" />
<Compile Include="TaskScheduler\Native\SYSTEMTIME.cs" />
<Compile Include="TaskScheduler\Native\WindowsImpersonatedIdentity.cs" />
<Compile Include="TaskScheduler\NotV1SupportedException.cs" />
<Compile Include="TaskScheduler\ReflectionHelper.cs" />
<Compile Include="TaskScheduler\ResourceReferenceValue.cs" />
<Compile Include="TaskScheduler\Task.cs" />
<Compile Include="TaskScheduler\TaskCollection.cs" />
<Compile Include="TaskScheduler\TaskEvent.cs" />
<Compile Include="TaskScheduler\TaskEventWatcher.cs" />
<Compile Include="TaskScheduler\TaskFolder.cs" />
<Compile Include="TaskScheduler\TaskFolderCollection.cs" />
<Compile Include="TaskScheduler\TaskHandlerInterfaces.cs" />
<Compile Include="TaskScheduler\TaskSecurity.cs" />
<Compile Include="TaskScheduler\TaskService.cs" />
<Compile Include="TaskScheduler\TaskServiceCronExt.cs" />
<Compile Include="TaskScheduler\Trigger.cs" />
<Compile Include="TaskScheduler\TriggerCollection.cs" />
<Compile Include="TaskScheduler\User.cs" />
<Compile Include="TaskScheduler\V1\TaskSchedulerV1Interop.cs" />
<Compile Include="TaskScheduler\V2\TaskSchedulerV2Interop.cs" />
<Compile Include="TaskScheduler\Wildcard.cs" />
<Compile Include="TaskScheduler\XmlSerializationHelper.cs" />
<Compile Include="Info\UserInfo\UserInfoHelper.cs" />
<Compile Include="Helpers\DomainHelper.cs" />
<Compile Include="Helpers\CheckRunner.cs" />
@ -245,6 +242,12 @@
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="TaskScheduler\V1\TaskSchedulerV1Schema.xsd">
<SubType>Designer</SubType>
</None>
<None Include="TaskScheduler\V2\TaskSchedulerV2Schema.xsd">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.de.resx" />