Update FlashPatcher

This commit is contained in:
SilicaAndPina 2021-05-11 18:45:56 +12:00
parent 1c30f93e8a
commit e6a15b22b3
77 changed files with 29873 additions and 5170 deletions

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E986744D-FB17-40E9-83DC-6043995545D9}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>FlashPatcher</RootNamespace>
<AssemblyName>Flash Pwner</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup />
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon0.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileUtil.cs" />
<Compile Include="FlashPwner.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="FlashPwner.Designer.cs">
<DependentUpon>FlashPwner.cs</DependentUpon>
</Compile>
<Compile Include="Privileges.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="FlashPwner.resx">
<DependentUpon>FlashPwner.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="app.manifest" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="icon0.ico" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -30,6 +30,7 @@
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -70,7 +71,8 @@
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>SilicaAndPina.pfx</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup>
<SignManifests>false</SignManifests>
@ -90,6 +92,8 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -110,6 +114,54 @@
<Compile Include="Privileges.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TaskService\Action.cs" />
<Compile Include="TaskService\ActionCollection.cs" />
<Compile Include="TaskService\CultureSwitcher.cs" />
<Compile Include="TaskService\EnumGlobalizer.cs" />
<Compile Include="TaskService\JetBrains.Annotations.cs" />
<Compile Include="TaskService\NamedValueCollection.cs" />
<Compile Include="TaskService\Native\AccessControlExtension.cs" />
<Compile Include="TaskService\Native\ADVAPI32.cs" />
<Compile Include="TaskService\Native\EnumUtil.cs" />
<Compile Include="TaskService\Native\EventLog.cs" />
<Compile Include="TaskService\Native\ExtensionAttributeFor.NET_2.0.cs" />
<Compile Include="TaskService\Native\Func.Net2.cs" />
<Compile Include="TaskService\Native\InteropUtil.cs" />
<Compile Include="TaskService\Native\KERNEL32.cs" />
<Compile Include="TaskService\Native\Linq.Net2.cs" />
<Compile Include="TaskService\Native\NetServerEnum.cs" />
<Compile Include="TaskService\Native\NTDSAPI.cs" />
<Compile Include="TaskService\Native\System.Collections.Generic.PreNet45.cs" />
<Compile Include="TaskService\Native\System.Collections.Specialized.Net20.cs" />
<Compile Include="TaskService\Native\System.Runtime.CompilerServices.CallerXXAttribute.PreNet45.cs" />
<Compile Include="TaskService\Native\SYSTEMTIME.cs" />
<Compile Include="TaskService\Native\ThreadLocal.PreNet40.cs" />
<Compile Include="TaskService\Native\WindowsImpersonatedIdentity.cs" />
<Compile Include="TaskService\NotV1SupportedException.cs" />
<Compile Include="TaskService\Properties\Resources.Designer.cs" />
<Compile Include="TaskService\ReflectionHelper.cs" />
<Compile Include="TaskService\ResourceReferenceValue.cs" />
<Compile Include="TaskService\Snapshot.cs" />
<Compile Include="TaskService\Task.cs" />
<Compile Include="TaskService\TaskCollection.cs" />
<Compile Include="TaskService\TaskEvent.cs" />
<Compile Include="TaskService\TaskEventWatcher.cs" />
<Compile Include="TaskService\TaskFolder.cs" />
<Compile Include="TaskService\TaskFolderCollection.cs" />
<Compile Include="TaskService\TaskHandlerInterfaces.cs" />
<Compile Include="TaskService\TaskSecurity.cs" />
<Compile Include="TaskService\TaskService.cs" />
<Compile Include="TaskService\TaskServiceCronExt.cs" />
<Compile Include="TaskService\TaskServiceFluentExt.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="TaskService\Trigger.cs" />
<Compile Include="TaskService\TriggerCollection.cs" />
<Compile Include="TaskService\User.cs" />
<Compile Include="TaskService\V1\TaskSchedulerV1Interop.cs" />
<Compile Include="TaskService\V2\TaskSchedulerV2Interop.cs" />
<Compile Include="TaskService\Wildcard.cs" />
<Compile Include="TaskService\XmlSerializationHelper.cs" />
<EmbeddedResource Include="FlashPatcherForm.resx">
<DependentUpon>FlashPatcherForm.cs</DependentUpon>
</EmbeddedResource>
@ -123,8 +175,8 @@
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<EmbeddedResource Include="TaskService\Properties\Resources.resx" />
<None Include="app.manifest" />
<None Include="FlashPatcher_TemporaryKey.pfx" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
@ -134,13 +186,20 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<None Include="SilicaAndPina.pfx" />
<None Include="TaskService\TaskService.csproj" />
<None Include="TaskService\V1\TaskSchedulerV1Schema.xsd">
<SubType>Designer</SubType>
</None>
<None Include="TaskService\V2\TaskSchedulerV2Schema.xsd">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="icon0.ico" />
<Content Include="TaskService\TaskService.bmp" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />

View File

@ -38,6 +38,7 @@
this.deleteFile = new System.Windows.Forms.Button();
this.progressBar = new System.Windows.Forms.ProgressBar();
this.projectorPatch = new System.Windows.Forms.Button();
this.UninstallNag = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// label1
@ -126,7 +127,7 @@
// projectorPatch
//
this.projectorPatch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.projectorPatch.Location = new System.Drawing.Point(422, 532);
this.projectorPatch.Location = new System.Drawing.Point(410, 528);
this.projectorPatch.Name = "projectorPatch";
this.projectorPatch.Size = new System.Drawing.Size(63, 19);
this.projectorPatch.TabIndex = 10;
@ -134,11 +135,22 @@
this.projectorPatch.UseVisualStyleBackColor = true;
this.projectorPatch.Click += new System.EventHandler(this.projectorPatch_Click);
//
// UninstallNag
//
this.UninstallNag.AutoSize = true;
this.UninstallNag.Location = new System.Drawing.Point(229, 530);
this.UninstallNag.Name = "UninstallNag";
this.UninstallNag.Size = new System.Drawing.Size(178, 17);
this.UninstallNag.TabIndex = 11;
this.UninstallNag.Text = "\"Uninstall Flash\" Nag Messages";
this.UninstallNag.UseVisualStyleBackColor = true;
//
// FlashPatcherForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(486, 552);
this.Controls.Add(this.UninstallNag);
this.Controls.Add(this.projectorPatch);
this.Controls.Add(this.progressBar);
this.Controls.Add(this.deleteFile);
@ -170,6 +182,7 @@
private System.Windows.Forms.Button deleteFile;
private System.Windows.Forms.ProgressBar progressBar;
private System.Windows.Forms.Button projectorPatch;
private System.Windows.Forms.CheckBox UninstallNag;
}
}

View File

@ -1,10 +1,11 @@
using System;
using Microsoft.Win32.TaskScheduler;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace FlashPatcher
@ -18,6 +19,14 @@ namespace FlashPatcher
}
public byte[] Timestamp = new byte[] { 0x00, 0x00, 0x40, 0x46, 0x3E, 0x6F, 0x77, 0x42 };
public byte[] Infintiy = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F };
/// <summary>
/// Locates a byte array inside a byte array,
/// used for finding the timestamp bytes
/// </summary>
/// <param name="data">data to search</param>
/// <param name="pattern">bytes to find</param>
/// <returns>Location of the found bytes, -1 if not found.</returns>
public Int64 GetPositionAfterMatch(byte[] data, byte[] pattern)
{
@ -40,7 +49,11 @@ namespace FlashPatcher
return -1;
}
/// <summary>
/// Uses windows security api's to give program access to the files
/// this is required becuase by default the ActiveX controls are owned by TrustedInstaller.
/// </summary>
/// <param name="filepath">the file to get access to</param>
public void TakeOwn(string filepath)
{
FileSecurity fileS = File.GetAccessControl(filepath);
@ -73,6 +86,11 @@ namespace FlashPatcher
File.SetAttributes(filepath, FileAttributes.Normal);
}
/// <summary>
/// Checks if a given file contains a killswitch timestamp.
/// </summary>
/// <param name="filepath">File to check.</param>
/// <returns></returns>
public bool CheckFileAndAdd(string filepath)
{
try
@ -92,6 +110,11 @@ namespace FlashPatcher
return false;
}
}
/// <summary>
/// Scans a folder for flash exectables, (OCX/DLL/EXE)
/// </summary>
/// <param name="path">Path to the folder</param>
public void ScanFolder(string path)
{
if(Directory.Exists(path))
@ -107,6 +130,11 @@ namespace FlashPatcher
}
}
/// <summary>
/// Patches a given Portable Executable (PE) file
/// </summary>
/// <param name="filepath">The path to the executable.</param>
/// <returns></returns>
public bool PatchExe(string filepath)
{
try
@ -143,26 +171,50 @@ namespace FlashPatcher
return true;
}
}
/// <summary>
/// Locates Portable Executables (PE) files
/// of adobe flash player.
/// </summary>
public void LocateExes()
{
string windir = Environment.GetEnvironmentVariable("WINDIR");
string localappdata = Environment.GetEnvironmentVariable("LOCALAPPDATA");
string flashPath = Path.Combine(windir, "System32", "Macromed", "Flash");
// Firefox / Internet Explorer
string flashPath = Path.Combine(windir, "System32", "Macromed", "Flash"); // Win64 in win64 mode, Win32 in win32 mode
ScanFolder(flashPath);
flashPath = Path.Combine(windir, "SysWOW64", "Macromed", "Flash");
flashPath = Path.Combine(windir, "Sysnative", "Macromed", "Flash"); // Win64 in win32 mode.
ScanFolder(flashPath);
flashPath = Path.Combine(windir, "SysWOW64", "Macromed", "Flash"); // Win32 in win64 mode
ScanFolder(flashPath);
// Chrome (<88)
flashPath = Path.Combine(localappdata, "Google", "Chrome", "User Data", "PepperFlash");
ScanFolder(flashPath);
// Edge
flashPath = Path.Combine(localappdata, "Microsoft", "Edge", "User Data", "PepperFlash");
ScanFolder(flashPath);
// Opera
flashPath = Path.Combine(localappdata, "Microsoft", "Opera Software", "Opera GX Stable", "User Data", "PepperFlash");
ScanFolder(flashPath);
flashPath = Path.Combine(localappdata, "Microsoft", "Opera Software", "Opera Stable", "User Data", "PepperFlash");
ScanFolder(flashPath);
}
private void FlashPwner_Load(object sender, EventArgs e)
{
LocateExes();
ScheduledTasksCheck();
}
private void defuseBomb_Click(object sender, EventArgs e)
@ -243,5 +295,36 @@ namespace FlashPatcher
MessageBox.Show("Patched! Projector should no longer open the browser!!!", "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void ScheduledTasksCheck()
{
TaskService service = new TaskService();
TaskCollection collection = service.RootFolder.GetTasks(new Regex("Adobe Flash Player .* Notifier"));
foreach(Task task in collection)
{
if (task.Enabled)
{
UninstallNag.Checked = true;
break;
}
}
this.UninstallNag.CheckedChanged += new EventHandler(this.UninstallNag_CheckedChanged);
}
private void UninstallNag_CheckedChanged(object sender, EventArgs e)
{
TaskService service = new TaskService();
TaskCollection collection = service.RootFolder.GetTasks(new Regex("Adobe Flash Player .* Notifier"));
foreach (Task task in collection)
task.Enabled = UninstallNag.Checked;
if(!UninstallNag.Checked)
MessageBox.Show("\"Please Uninstall Flash\" Nag Messages DISABLED.", "Nag Messages",MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("\"Please Uninstall Flash\" Nag Messages ENABLED.", "Nag Messages", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}

View File

@ -1,175 +0,0 @@
namespace FlashPatcher
{
partial class FlashPwner
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FlashPwner));
this.label1 = new System.Windows.Forms.Label();
this.addFile = new System.Windows.Forms.Button();
this.cringe = new System.Windows.Forms.Label();
this.defuseBomb = new System.Windows.Forms.Button();
this.console = new System.Windows.Forms.TextBox();
this.flashExes = new System.Windows.Forms.ListBox();
this.deleteFile = new System.Windows.Forms.Button();
this.progressBar = new System.Windows.Forms.ProgressBar();
this.projectorPatch = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(9, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(263, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Flash Executables: (pepflash.dll, NPSWF64, flash.ocx)";
//
// addFile
//
this.addFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.addFile.Location = new System.Drawing.Point(434, 2);
this.addFile.Name = "addFile";
this.addFile.Size = new System.Drawing.Size(39, 20);
this.addFile.TabIndex = 1;
this.addFile.Text = "Add ";
this.addFile.UseVisualStyleBackColor = true;
this.addFile.Click += new System.EventHandler(this.addFile_Click);
//
// cringe
//
this.cringe.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.cringe.AutoSize = true;
this.cringe.Location = new System.Drawing.Point(2, 530);
this.cringe.Name = "cringe";
this.cringe.Size = new System.Drawing.Size(211, 13);
this.cringe.TabIndex = 4;
this.cringe.Text = "The quieter you becom, the moar u cn hear";
//
// defuseBomb
//
this.defuseBomb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.defuseBomb.Location = new System.Drawing.Point(11, 468);
this.defuseBomb.Name = "defuseBomb";
this.defuseBomb.Size = new System.Drawing.Size(462, 23);
this.defuseBomb.TabIndex = 5;
this.defuseBomb.Text = "!!! DEFUSE THE BOMB !!!";
this.defuseBomb.UseVisualStyleBackColor = true;
this.defuseBomb.Click += new System.EventHandler(this.defuseBomb_Click);
//
// console
//
this.console.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.console.Location = new System.Drawing.Point(12, 178);
this.console.Multiline = true;
this.console.Name = "console";
this.console.Size = new System.Drawing.Size(462, 284);
this.console.TabIndex = 6;
//
// flashExes
//
this.flashExes.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flashExes.FormattingEnabled = true;
this.flashExes.HorizontalScrollbar = true;
this.flashExes.Location = new System.Drawing.Point(12, 25);
this.flashExes.Name = "flashExes";
this.flashExes.Size = new System.Drawing.Size(462, 147);
this.flashExes.TabIndex = 7;
//
// deleteFile
//
this.deleteFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.deleteFile.Location = new System.Drawing.Point(386, 2);
this.deleteFile.Name = "deleteFile";
this.deleteFile.Size = new System.Drawing.Size(48, 20);
this.deleteFile.TabIndex = 8;
this.deleteFile.Text = "Delete";
this.deleteFile.UseVisualStyleBackColor = true;
this.deleteFile.Click += new System.EventHandler(this.deleteFile_Click);
//
// progressBar
//
this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBar.Location = new System.Drawing.Point(14, 497);
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(459, 23);
this.progressBar.TabIndex = 9;
//
// projectorPatch
//
this.projectorPatch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.projectorPatch.Location = new System.Drawing.Point(422, 532);
this.projectorPatch.Name = "projectorPatch";
this.projectorPatch.Size = new System.Drawing.Size(63, 19);
this.projectorPatch.TabIndex = 10;
this.projectorPatch.Text = "Projector+";
this.projectorPatch.UseVisualStyleBackColor = true;
this.projectorPatch.Click += new System.EventHandler(this.projectorPatch_Click);
//
// FlashPwner
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(486, 552);
this.Controls.Add(this.projectorPatch);
this.Controls.Add(this.progressBar);
this.Controls.Add(this.deleteFile);
this.Controls.Add(this.flashExes);
this.Controls.Add(this.console);
this.Controls.Add(this.defuseBomb);
this.Controls.Add(this.cringe);
this.Controls.Add(this.addFile);
this.Controls.Add(this.label1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximumSize = new System.Drawing.Size(1920, 591);
this.MinimumSize = new System.Drawing.Size(502, 591);
this.Name = "FlashPwner";
this.Text = "Flash Pwner";
this.Load += new System.EventHandler(this.FlashPwner_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button addFile;
private System.Windows.Forms.Label cringe;
private System.Windows.Forms.Button defuseBomb;
private System.Windows.Forms.TextBox console;
private System.Windows.Forms.ListBox flashExes;
private System.Windows.Forms.Button deleteFile;
private System.Windows.Forms.ProgressBar progressBar;
private System.Windows.Forms.Button projectorPatch;
}
}

View File

@ -1,248 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Windows.Forms;
namespace FlashPatcher
{
public partial class FlashPwner : Form
{
public FlashPwner()
{
InitializeComponent();
}
public byte[] Timestamp = new byte[] { 0x00, 0x00, 0x40, 0x46, 0x3E, 0x6F, 0x77, 0x42 };
public byte[] Infintiy = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F };
public Int64 GetPositionAfterMatch(byte[] data, byte[] pattern)
{
for (Int64 i = 0; i < data.LongLength - pattern.LongLength; i++)
{
bool match = true;
for (Int64 k = 0; k < pattern.LongLength; k++)
{
if (data[i + k] != pattern[k])
{
match = false;
break;
}
}
if (match)
{
return i;
}
}
return -1;
}
public void TakeOwn(string filepath)
{
FileSecurity fileS = File.GetAccessControl(filepath);
SecurityIdentifier cu = WindowsIdentity.GetCurrent().User;
SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
try
{
Privileges.EnablePrivilege(SecurityEntity.SE_TAKE_OWNERSHIP_NAME);
}
catch(Exception)
{
console.AppendText("Failed to get SeTakeOwnershipPrivledge\r\n");
}
fileS.SetOwner(cu);
File.SetAccessControl(filepath, fileS);
fileS.SetAccessRuleProtection(false, false);
fileS.RemoveAccessRuleAll(new FileSystemAccessRule(everyone, FileSystemRights.FullControl, AccessControlType.Deny));
fileS.RemoveAccessRuleAll(new FileSystemAccessRule(cu, FileSystemRights.FullControl, AccessControlType.Deny));
fileS.SetAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.FullControl, AccessControlType.Allow));
fileS.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.FullControl, AccessControlType.Allow));
File.SetAccessControl(filepath, fileS);
File.SetAttributes(filepath, FileAttributes.Normal);
}
public bool CheckFileAndAdd(string filepath)
{
try
{
byte[] fileData = File.ReadAllBytes(filepath);
Int64 timestampLocation = GetPositionAfterMatch(fileData, Timestamp);
if (timestampLocation != -1)
{
flashExes.Items.Add(filepath);
console.AppendText("Found killswitch timestamp in " + Path.GetFileName(filepath) + " @ 0x" + timestampLocation.ToString("X") + "\r\n");
return true;
}
return false;
}
catch(Exception)
{
return false;
}
}
public void ScanFolder(string path)
{
if(Directory.Exists(path))
{
String[] fileList = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
foreach (string file in fileList)
{
if (file.ToLower().EndsWith(".ocx") || file.ToLower().EndsWith(".dll") || file.ToLower().EndsWith(".exe"))
{
CheckFileAndAdd(file);
}
}
}
}
public bool PatchExe(string filepath)
{
try
{
Process[] lockingProcesses = FileUtil.WhoIsLocking(filepath).ToArray();
foreach(Process proc in lockingProcesses)
{
DialogResult res = MessageBox.Show("Flash is currently in use by (" + proc.Id.ToString() + ")" + proc.ProcessName + "\nEnd Process?", "File in use :/", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (res == DialogResult.Yes)
proc.Kill(); // DIE HHAHA
else
return true;
}
byte[] fileData = File.ReadAllBytes(filepath);
Int64 timestampLocation = GetPositionAfterMatch(fileData, Timestamp);
TakeOwn(filepath);
FileStream fs = File.OpenWrite(filepath);
fs.Seek(timestampLocation, SeekOrigin.Begin);
fs.Write(Infintiy, 0x00, Infintiy.Length);
fs.Close();
console.AppendText("Patched: " + Path.GetFileName(filepath) + ".\r\n");
flashExes.Items.Remove(filepath);
Application.DoEvents();
progressBar.Increment(1);
return false;
}
catch(Exception e)
{
MessageBox.Show(e.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
return true;
}
}
public void LocateExes()
{
string windir = Environment.GetEnvironmentVariable("WINDIR");
string localappdata = Environment.GetEnvironmentVariable("LOCALAPPDATA");
string flashPath = Path.Combine(windir, "System32", "Macromed", "Flash");
ScanFolder(flashPath);
flashPath = Path.Combine(windir, "SysWOW64", "Macromed", "Flash");
ScanFolder(flashPath);
flashPath = Path.Combine(localappdata, "Google", "Chrome", "User Data", "PepperFlash");
ScanFolder(flashPath);
flashPath = Path.Combine(localappdata, "Microsoft", "Edge", "User Data", "PepperFlash");
ScanFolder(flashPath);
}
private void FlashPwner_Load(object sender, EventArgs e)
{
LocateExes();
}
private void defuseBomb_Click(object sender, EventArgs e)
{
defuseBomb.Enabled = false;
if(flashExes.Items.Count > 0)
{
progressBar.Maximum = flashExes.Items.Count;
List<string> copyFlashExes = new List<string>();
foreach (string flashExe in flashExes.Items)
{
copyFlashExes.Add(flashExe);
}
bool errored = false;
foreach (string flashExe in copyFlashExes)
{
errored = PatchExe(flashExe);
}
if(!errored)
MessageBox.Show("Patched! Your flash should work again!!!", "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("No files to patch!", "File Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
defuseBomb.Enabled = true;
}
private void addFile_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Flash Executable";
ofd.Filter = "PE Executables (*.exe, *.ocx, *.dll)|*.dll;*.exe;*.ocx|ELF Executables (*.so, *.elf, *.dylib)|*.so;*.elf;*.dylib";
DialogResult res = ofd.ShowDialog();
if(res == DialogResult.OK)
{
if (!CheckFileAndAdd(ofd.FileName))
{
MessageBox.Show("File selected does not contain the killswitch timestamp, cannot patch!", "Timestamp Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void patchProjetor(string path)
{
byte[] projBytes = File.ReadAllBytes(path);
byte[] getUrlPattern = new byte[] { 0xF4, 0xE8, 0xBE, 0xFE, 0xFF, 0xFF };
byte[] nops = new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
Int64 getUrlLocation = GetPositionAfterMatch(projBytes, getUrlPattern);
if (getUrlLocation == -1)
return;
FileStream fs = File.OpenWrite(path);
fs.Seek(getUrlLocation+1, SeekOrigin.Begin);
fs.Write(nops, 0x00, nops.Length);
fs.Close();
}
private void deleteFile_Click(object sender, EventArgs e)
{
if(flashExes.SelectedIndex >= 0)
flashExes.Items.RemoveAt(flashExes.SelectedIndex);
}
private void projectorPatch_Click(object sender, EventArgs e)
{
MessageBox.Show("This is a patch for the standalone \"projector\" program from adobe, it stops it opening your browser whenever a game tries to call javascript with getURL()\n\nNote: the projector DOES NOT HAVE A KILLSWITCH/TIMEBOMB and this is not needed to use the Flash Projector.", "Projector", MessageBoxButtons.OK, MessageBoxIcon.Information);
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Flash Projector";
ofd.Filter = "PE Executables (*.exe)|*.exe;|ELF Executables (*.elf)|*.elf";
DialogResult res = ofd.ShowDialog();
if(res == DialogResult.OK)
{
patchProjetor(ofd.FileName);
MessageBox.Show("Patched! Projector should no longer open the browser!!!", "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,771 @@
using JetBrains.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
namespace Microsoft.Win32.TaskScheduler
{
/// <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.
/// </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.
/// </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.
/// </summary>
Version2 = 2,
/// <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>
[XmlRoot("Actions", Namespace = TaskDefinition.tns, IsNullable = false)]
[PublicAPI]
public sealed class ActionCollection : IList<Action>, IDisposable, IXmlSerializable, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
internal const int MaxActions = 32;
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] V1Interop.ITask task)
{
v1Task = task;
v1Actions = GetV1Actions();
PowerShellConversion = Action.TryParse(v1Task.GetDataItem(nameof(PowerShellConversion)), psConvert | PowerShellActionPlatformOption.Version2);
}
internal ActionCollection([NotNull] V2Interop.ITaskDefinition iTaskDef)
{
v2Def = iTaskDef;
v2Coll = iTaskDef.Actions;
System.Text.RegularExpressions.Match match;
if (iTaskDef.Data != null && (match = System.Text.RegularExpressions.Regex.Match(iTaskDef.Data, psV2IdRegex)).Success)
{
var on = false;
try { on = bool.Parse(match.Groups["v"].Value); } catch { try { on = int.Parse(match.Groups["v"].Value) == 1; } catch { } }
if (on)
psConvert |= PowerShellActionPlatformOption.Version2;
else
psConvert &= ~PowerShellActionPlatformOption.Version2;
}
UnconvertUnsupportedActions();
}
/// <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]
public string Context
{
get
{
if (v2Coll != null)
return v2Coll.Context;
return v1Task.GetDataItem("ActionCollectionContext");
}
set
{
if (v2Coll != null)
v2Coll.Context = value;
else
v1Task.SetDataItem("ActionCollectionContext", value);
OnNotifyPropertyChanged();
}
}
/// <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>
[DefaultValue(typeof(PowerShellActionPlatformOption), "Version2")]
public PowerShellActionPlatformOption PowerShellConversion
{
get => psConvert;
set
{
if (psConvert != value)
{
psConvert = value;
if (v1Task != null)
v1Task.SetDataItem(nameof(PowerShellConversion), value.ToString());
if (v2Def != null)
{
if (!string.IsNullOrEmpty(v2Def.Data))
v2Def.Data = System.Text.RegularExpressions.Regex.Replace(v2Def.Data, psV2IdRegex, "");
if (!SupportV2Conversion)
v2Def.Data = string.Format("{0}; {1}=0", v2Def.Data, nameof(PowerShellConversion));
}
OnNotifyPropertyChanged();
}
}
}
/// <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;
/// <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
{
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>
/// <value>The zero-based index of the action to get or set.</value>
[NotNull]
public Action this[int index]
{
get
{
if (v2Coll != null)
return Action.CreateAction(v2Coll[++index]);
if (v1Task != null)
{
if (SupportV1Conversion)
return v1Actions[index];
else
{
if (index == 0)
return v1Actions[0];
}
}
throw new ArgumentOutOfRangeException();
}
set
{
if (index < 0 || Count <= index)
throw new ArgumentOutOfRangeException(nameof(index), index, "Index is not a valid index in the ActionCollection");
var orig = this[index].Clone();
if (v2Coll != null)
{
inV2set = true;
try
{
Insert(index, value);
RemoveAt(index + 1);
}
finally
{
inV2set = false;
}
}
else
{
v1Actions[index] = value;
SaveV1Actions();
}
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index));
}
}
object IList.this[int index]
{
get => this[index];
set => this[index] = (Action)value;
}
/// <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>
[NotNull]
public TAction Add<TAction>([NotNull] TAction action) where TAction : Action
{
if (action == null)
throw new ArgumentNullException(nameof(action));
if (v2Def != null)
action.Bind(v2Def);
else
{
if (!SupportV1Conversion && (v1Actions.Count >= 1 || !(action is ExecAction)))
throw new NotV1SupportedException($"Only a single {nameof(ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value.");
v1Actions.Add(action);
SaveV1Actions();
}
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action));
return action;
}
/// <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]
public Action AddNew(TaskActionType actionType)
{
if (Count >= MaxActions)
throw new ArgumentOutOfRangeException(nameof(actionType), "A maximum of 32 actions is allowed within a single task.");
if (v1Task != null)
{
if (!SupportV1Conversion && (v1Actions.Count >= 1 || actionType != TaskActionType.Execute))
throw new NotV1SupportedException($"Only a single {nameof(ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value.");
return Action.CreateAction(v1Task);
}
return Action.CreateAction(v2Coll.Create(actionType));
}
/// <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)
v2Coll.Clear();
else
{
v1Actions.Clear();
SaveV1Actions();
}
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <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="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);
/// <summary>
/// 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="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="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)
throw new ArgumentNullException(nameof(array));
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException(nameof(index));
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
if (count < 0 || count > (Count - index))
throw new ArgumentOutOfRangeException(nameof(count));
if ((Count - index) > (array.Length - arrayIndex))
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
for (var i = 0; i < count; i++)
array[arrayIndex + i] = (Action)this[index + i].Clone();
}
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
v1Task = null;
v2Def = null;
if (v2Coll != null) Marshal.ReleaseComObject(v2Coll);
}
/// <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.
/// </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>
public Action Find(Predicate<Action> match)
{
if (match == null)
throw new ArgumentNullException(nameof(match));
foreach (var item in this)
if (match(item)) return item;
return null;
}
/// <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.
/// </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>
public int FindIndexOf(int startIndex, int count, [NotNull] Predicate<Action> match)
{
if (startIndex < 0 || startIndex >= Count)
throw new ArgumentOutOfRangeException(nameof(startIndex));
if (startIndex + count > Count)
throw new ArgumentOutOfRangeException(nameof(count));
if (match == null)
throw new ArgumentNullException(nameof(match));
for (var i = startIndex; i < startIndex + count; i++)
if (match(this[i])) return i;
return -1;
}
/// <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.
/// </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>
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>
public IEnumerator<Action> GetEnumerator()
{
if (v2Coll != null)
return new ComEnumerator<Action, V2Interop.IAction>(() => v2Coll.Count, i => v2Coll[i], Action.CreateAction);
return v1Actions.GetEnumerator();
}
/// <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>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)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException(nameof(index));
if (v2Coll != null)
{
var pushItems = new Action[Count - index];
if (Count != index)
{
CopyTo(index, pushItems, 0, Count - index);
for (var j = Count - 1; j >= index; j--)
RemoveAt(j);
}
Add(action);
if (Count != index)
for (var k = 0; k < pushItems.Length; k++)
Add(pushItems[k]);
}
else
{
if (!SupportV1Conversion && (index > 0 || !(action is ExecAction)))
throw new NotV1SupportedException($"Only a single {nameof(ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value.");
v1Actions.Insert(index, action);
SaveV1Actions();
}
if (!inV2set)
{
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, action, index));
}
}
/// <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] Action item)
{
var idx = IndexOf(item);
if (idx != -1)
{
try
{
RemoveAt(idx);
return true;
}
catch { }
}
return false;
}
/// <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 < 0 || index >= Count)
throw new ArgumentOutOfRangeException(nameof(index), index, "Failed to remove action. Index out of range.");
var item = this[index].Clone();
if (v2Coll != null)
v2Coll.Remove(++index);
else
{
v1Actions.RemoveAt(index);
SaveV1Actions();
}
if (!inV2set)
{
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
}
/// <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 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 (var i = 0; i < Count; i++)
{
var action = this[i];
var bindable = action as IBindAsExecAction;
if (bindable != null && !(action is ComHandlerAction))
this[i] = ExecAction.ConvertToPowerShellAction(action);
}
}
}
private List<Action> GetV1Actions()
{
var ret = new List<Action>();
if (v1Task != null && v1Task.GetDataItem("ActionType") != "EMPTY")
{
var exec = new ExecAction(v1Task);
var items = exec.ParsePowerShellItems();
if (items != null)
{
if (items.Length == 2 && items[0] == "MULTIPLE")
{
PowerShellConversion |= PowerShellActionPlatformOption.Version1;
var mc = System.Text.RegularExpressions.Regex.Matches(items[1], @"<# (?<id>\w+):(?<t>\w+) #>\s*(?<c>[^<#]*)\s*");
foreach (System.Text.RegularExpressions.Match ms in mc)
{
var a = Action.ActionFromScript(ms.Groups["t"].Value, ms.Groups["c"].Value);
if (a != null)
{
if (ms.Groups["id"].Value != "NO_ID")
a.Id = ms.Groups["id"].Value;
ret.Add(a);
}
}
}
else
ret.Add(ExecAction.ConvertFromPowerShellAction(exec));
}
else if (!string.IsNullOrEmpty(exec.Path))
{
ret.Add(exec);
}
}
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)
throw new ArgumentNullException(nameof(v1Task));
if (v1Actions.Count == 0)
{
v1Task.SetApplicationName(string.Empty);
v1Task.SetParameters(string.Empty);
v1Task.SetWorkingDirectory(string.Empty);
v1Task.SetDataItem("ActionId", null);
v1Task.SetDataItem("ActionType", "EMPTY");
}
else if (v1Actions.Count == 1)
{
if (!SupportV1Conversion && v1Actions[0].ActionType != TaskActionType.Execute)
throw new NotV1SupportedException($"Only a single {nameof(ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value.");
v1Task.SetDataItem("ActionType", null);
v1Actions[0].Bind(v1Task);
}
else
{
if (!SupportV1Conversion)
throw new NotV1SupportedException($"Only a single {nameof(ExecAction)} is supported unless the {nameof(PowerShellConversion)} property includes the {nameof(PowerShellActionPlatformOption.Version1)} value.");
// Build list of internal PowerShell scripts
var sb = new System.Text.StringBuilder();
foreach (var item in v1Actions)
sb.Append($"<# {item.Id ?? "NO_ID"}:{item.ActionType} #> {item.GetPowerShellCommand()} ");
// Build and save PS ExecAction
var ea = ExecAction.CreatePowerShellAction("MULTIPLE", sb.ToString());
ea.Bind(v1Task);
v1Task.SetDataItem("ActionId", null);
v1Task.SetDataItem("ActionType", "MULTIPLE");
}
}
private void UnconvertUnsupportedActions()
{
if (TaskService.LibraryVersion.Minor > 3)
{
for (var i = 0; i < Count; i++)
{
var action = this[i] as ExecAction;
if (action != null)
{
var newAction = Action.ConvertFromPowerShellAction(action);
if (newAction != null)
this[i] = newAction;
}
}
}
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Threading;
namespace Microsoft.Win32.TaskScheduler
{
internal class CultureSwitcher : IDisposable
{
private readonly System.Globalization.CultureInfo cur, curUI;
public CultureSwitcher([JetBrains.Annotations.NotNull] System.Globalization.CultureInfo culture)
{
cur = Thread.CurrentThread.CurrentCulture;
curUI = Thread.CurrentThread.CurrentUICulture;
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = culture;
}
public void Dispose()
{
Thread.CurrentThread.CurrentCulture = cur;
Thread.CurrentThread.CurrentUICulture = curUI;
}
}
}

View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Functions to provide localized strings for enumerated types and values.
/// </summary>
public static class TaskEnumGlobalizer
{
/// <summary>
/// Gets a string representing the localized value of the provided enum.
/// </summary>
/// <param name="enumValue">The enum value.</param>
/// <returns>A localized string, if available.</returns>
public static string GetString(object enumValue)
{
switch (enumValue.GetType().Name)
{
case "DaysOfTheWeek":
return GetCultureEquivalentString((DaysOfTheWeek)enumValue);
case "MonthsOfTheYear":
return GetCultureEquivalentString((MonthsOfTheYear)enumValue);
case "TaskTriggerType":
return BuildEnumString("TriggerType", enumValue);
case "WhichWeek":
return BuildEnumString("WW", enumValue);
case "TaskActionType":
return BuildEnumString("ActionType", enumValue);
case "TaskState":
return BuildEnumString("TaskState", enumValue);
}
return enumValue.ToString();
}
private static string GetCultureEquivalentString(DaysOfTheWeek val)
{
if (val == DaysOfTheWeek.AllDays)
return Properties.Resources.DOWAllDays;
var s = new List<string>(7);
var vals = Enum.GetValues(val.GetType());
for (var i = 0; i < vals.Length - 1; i++)
{
if ((val & (DaysOfTheWeek)vals.GetValue(i)) > 0)
s.Add(DateTimeFormatInfo.CurrentInfo.GetDayName((DayOfWeek)i));
}
return string.Join(Properties.Resources.ListSeparator, s.ToArray());
}
private static string GetCultureEquivalentString(MonthsOfTheYear val)
{
if (val == MonthsOfTheYear.AllMonths)
return Properties.Resources.MOYAllMonths;
var s = new List<string>(12);
var vals = Enum.GetValues(val.GetType());
for (var i = 0; i < vals.Length - 1; i++)
{
if ((val & (MonthsOfTheYear)vals.GetValue(i)) > 0)
s.Add(DateTimeFormatInfo.CurrentInfo.GetMonthName(i+1));
}
return string.Join(Properties.Resources.ListSeparator, s.ToArray());
}
private static string BuildEnumString(string preface, object enumValue)
{
var vals = enumValue.ToString().Split(new[] { ", " }, StringSplitOptions.None);
if (vals.Length == 0)
return string.Empty;
for (var i = 0; i < vals.Length; i++)
vals[i] = Properties.Resources.ResourceManager.GetString(preface + vals[i]);
return string.Join(Properties.Resources.ListSeparator, vals);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,616 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
using Microsoft.Win32.TaskScheduler.V2Interop;
using JetBrains.Annotations;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Pair of name and value.
/// </summary>
[PublicAPI]
public class NameValuePair : IXmlSerializable, INotifyPropertyChanged, ICloneable, IEquatable<NameValuePair>, IEquatable<ITaskNamedValuePair>
{
private readonly ITaskNamedValuePair v2Pair;
private string name, value;
/// <summary>
/// Occurs when a property has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Initializes a new instance of the <see cref="NameValuePair"/> class.
/// </summary>
public NameValuePair() { }
internal NameValuePair([NotNull] ITaskNamedValuePair iPair)
{
v2Pair = iPair;
}
internal NameValuePair([NotNull] string name, [NotNull] string value)
{
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value))
throw new ArgumentException("Both name and value must be non-empty strings.");
this.name = name; this.value = value;
}
[XmlIgnore]
internal bool AttributedXmlFormat { get; set; } = true;
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
[NotNull]
public string Name
{
get { return v2Pair == null ? name : v2Pair.Name; }
set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Name)); if (v2Pair == null) name = value; else v2Pair.Name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); }
}
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
[NotNull]
public string Value
{
get { return v2Pair == null ? value : v2Pair.Value; }
set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Value)); if (v2Pair == null) this.value = value; else v2Pair.Value = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); }
}
/// <summary>
/// Clones this instance.
/// </summary>
/// <returns>A copy of an unbound <see cref="NameValuePair"/>.</returns>
[NotNull]
public NameValuePair Clone() => new NameValuePair(Name, Value);
object ICloneable.Clone() => Clone();
/// <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>
public override bool Equals(object obj)
{
var valuePair = obj as ITaskNamedValuePair;
if (valuePair != null)
return (this as IEquatable<ITaskNamedValuePair>).Equals(valuePair);
var pair = obj as NameValuePair;
if (pair != null)
return Equals(pair);
return base.Equals(obj);
}
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
public bool Equals([NotNull] NameValuePair other) => other.Name == Name && other.Value == Value;
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
bool IEquatable<ITaskNamedValuePair>.Equals(ITaskNamedValuePair other) => other.Name == Name && other.Value == Value;
/// <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 = Name, B = Value }.GetHashCode();
/// <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() => $"{Name}={Value}";
/// <summary>
/// Implements the operator implicit NameValuePair.
/// </summary>
/// <param name="kvp">The KeyValuePair.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static implicit operator NameValuePair(KeyValuePair<string, string> kvp) => new NameValuePair(kvp.Key, kvp.Value);
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null;
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Value")
{
Name = reader.GetAttribute("name");
Value = reader.ReadString();
reader.Read();
}
else
{
reader.ReadStartElement();
XmlSerializationHelper.ReadObjectProperties(reader, this);
reader.ReadEndElement();
}
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
if (AttributedXmlFormat)
{
writer.WriteAttributeString("name", Name);
writer.WriteString(Value);
}
else
{
XmlSerializationHelper.WriteObjectProperties(writer, this);
}
}
}
/// <summary>
/// Contains a collection of name-value pairs.
/// </summary>
[PublicAPI]
public sealed class NamedValueCollection : IDisposable, ICollection<NameValuePair>, IDictionary<string, string>, INotifyCollectionChanged, INotifyPropertyChanged
{
private ITaskNamedValueCollection v2Coll;
private readonly List<NameValuePair> unboundDict;
/// <summary>
/// Occurs when the collection has changed.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>
/// Occurs when a property has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
internal NamedValueCollection([NotNull] ITaskNamedValueCollection iColl) { v2Coll = iColl; }
internal NamedValueCollection()
{
unboundDict = new List<NameValuePair>(5);
}
[XmlIgnore]
internal bool AttributedXmlFormat { get; set; } = true;
internal void Bind([NotNull] ITaskNamedValueCollection iTaskNamedValueCollection)
{
v2Coll = iTaskNamedValueCollection;
v2Coll.Clear();
foreach (var item in unboundDict)
v2Coll.Create(item.Name, item.Value);
}
/// <summary>
/// Copies current <see cref="NamedValueCollection"/> to another.
/// </summary>
/// <param name="destCollection">The destination collection.</param>
public void CopyTo([NotNull] NamedValueCollection destCollection)
{
if (v2Coll != null)
{
for (var i = 1; i <= Count; i++)
destCollection.Add(v2Coll[i].Name, v2Coll[i].Value);
}
else
{
foreach (var item in unboundDict)
destCollection.Add(item.Name, item.Value);
}
}
/// <summary>
/// Releases all resources used by this class.
/// </summary>
public void Dispose()
{
if (v2Coll != null) Marshal.ReleaseComObject(v2Coll);
}
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
public int Count => v2Coll?.Count ?? unboundDict.Count;
/// <summary>
/// Gets a collection of the names.
/// </summary>
/// <value>
/// The names.
/// </value>
[ItemNotNull, NotNull]
public ICollection<string> Names
{
get
{
if (v2Coll == null)
return unboundDict.ConvertAll(p => p.Name);
var ret = new List<string>(v2Coll.Count);
foreach (var item in this)
ret.Add(item.Name);
return ret;
}
}
/// <summary>
/// Gets a collection of the values.
/// </summary>
/// <value>
/// The values.
/// </value>
[ItemNotNull, NotNull]
public ICollection<string> Values
{
get
{
if (v2Coll == null)
return unboundDict.ConvertAll(p => p.Value);
var ret = new List<string>(v2Coll.Count);
foreach (var item in this)
ret.Add(item.Value);
return ret;
}
}
/// <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.
/// </summary>
/// <param name="name">Name to get the value for.</param>
/// <returns>Value for the name, or null if not found.</returns>
public string this[string name]
{
[CanBeNull]
get
{
string ret;
TryGetValue(name, out ret);
return ret;
}
[NotNull]
set
{
int idx;
NameValuePair old = null;
var nvp = new NameValuePair(name, value);
if (v2Coll == null)
{
idx = unboundDict.FindIndex(p => p.Name == name);
if (idx == -1)
unboundDict.Add(nvp);
else
{
old = unboundDict[idx];
unboundDict[idx] = nvp;
}
}
else
{
var array = new KeyValuePair<string, string>[Count];
((ICollection<KeyValuePair<string, string>>)this).CopyTo(array, 0);
idx = Array.FindIndex(array, p => p.Key == name);
if (idx == -1)
v2Coll.Create(name, value);
else
{
old = array[idx];
array[idx] = new KeyValuePair<string, string>(name, value);
v2Coll.Clear();
foreach (KeyValuePair<string, string> t in array)
v2Coll.Create(t.Key, t.Value);
}
}
if (idx == -1)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, nvp));
else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, nvp, old, idx));
}
}
/// <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>
public void Add([NotNull] NameValuePair item)
{
if (v2Coll != null)
v2Coll.Create(item.Name, item.Value);
else
unboundDict.Add(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
/// <summary>
/// Adds a name-value pair to the collection.
/// </summary>
/// <param name="name">The name associated with a value in a name-value pair.</param>
/// <param name="value">The value associated with a name in a name-value pair.</param>
public void Add([NotNull] string name, [NotNull] string value)
{
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>
public void Clear()
{
if (v2Coll != null)
v2Coll.Clear();
else
unboundDict.Clear();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
/// </returns>
public IEnumerator<NameValuePair> GetEnumerator()
{
if (v2Coll == null)
return unboundDict.GetEnumerator();
return new ComEnumerator<NameValuePair, ITaskNamedValuePair>(() => v2Coll.Count, i => v2Coll[i], o => new NameValuePair(o));
}
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (NameValuePair item in e.NewItems)
item.AttributedXmlFormat = AttributedXmlFormat;
CollectionChanged?.Invoke(this, e);
}
/// <summary>
/// Removes the name-value pair with the specified key from the collection.
/// </summary>
/// <param name="name">The name associated with a value in a name-value pair.</param>
/// <returns><c>true</c> if item successfully removed; <c>false</c> otherwise.</returns>
public bool Remove([NotNull] string name)
{
var i = -1;
NameValuePair nvp = null;
try
{
if (v2Coll == null)
{
i = unboundDict.FindIndex(p => p.Name == name);
if (i != -1)
{
nvp = unboundDict[i];
unboundDict.RemoveAt(i);
}
return (i != -1);
}
for (i = 0; i < v2Coll.Count; i++)
{
if (name == v2Coll[i].Name)
{
nvp = new NameValuePair(v2Coll[i]).Clone();
v2Coll.Remove(i);
return true;
}
}
i = -1;
}
finally
{
if (i != -1)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, nvp, i));
}
}
return false;
}
/// <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.
/// </summary>
/// <param name="name">The name whose value to get.</param>
/// <param name="value">When this method returns, the value associated with the specified name, if the name is found; otherwise, <c>null</c>. This parameter is passed uninitialized.</param>
/// <returns><c>true</c> if the collection contains an element with the specified name; otherwise, <c>false</c>.</returns>
public bool TryGetValue(string name, out string value)
{
if (v2Coll != null)
{
foreach (var item in this)
{
if (string.CompareOrdinal(item.Name, name) == 0)
{
value = item.Value;
return true;
}
}
value = null;
return false;
}
var nvp = unboundDict.Find(p => p.Name == name);
value = nvp?.Value;
return nvp != null;
}
/// <summary>
/// Gets the collection enumerator for the name-value collection.
/// </summary>
/// <returns>An <see cref="System.Collections.IEnumerator"/> for the collection.</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
bool ICollection<NameValuePair>.Contains(NameValuePair item)
{
if (v2Coll == null)
return unboundDict.Contains(item);
foreach (var invp in this)
if (Equals(item, invp)) return true;
return false;
}
void ICollection<NameValuePair>.CopyTo(NameValuePair[] array, int arrayIndex)
{
if (v2Coll == null)
unboundDict.CopyTo(array, arrayIndex);
else
{
if (array.Length - arrayIndex < v2Coll.Count)
throw new ArgumentException("Items in collection exceed available items in destination array.");
if (arrayIndex < 0)
throw new ArgumentException(@"Array index must be 0 or greater.", nameof(arrayIndex));
for (var i = 0; i < v2Coll.Count; i++)
array[i + arrayIndex] = new NameValuePair(v2Coll[i]);
}
}
bool ICollection<NameValuePair>.IsReadOnly => false;
ICollection<string> IDictionary<string, string>.Keys => Names;
bool ICollection<KeyValuePair<string, string>>.IsReadOnly => false;
bool ICollection<NameValuePair>.Remove(NameValuePair item)
{
var i = -1;
try
{
if (v2Coll == null)
{
if ((i = unboundDict.IndexOf(item)) != -1)
return unboundDict.Remove(item);
}
else
{
for (i = 0; i < v2Coll.Count; i++)
{
if (item.Equals(v2Coll[i]))
{
v2Coll.Remove(i);
return true;
}
}
}
i = -1;
}
finally
{
if (i != -1)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, i));
}
}
return false;
}
bool IDictionary<string, string>.ContainsKey(string key) => Names.Contains(key);
void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item) =>
((ICollection<NameValuePair>)this).Contains(new NameValuePair(item.Key, item.Value));
void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
if (array.Length < Count + arrayIndex)
throw new ArgumentOutOfRangeException(nameof(array), @"Array has insufficient capacity to support copy.");
foreach (var item in ((IEnumerable<KeyValuePair<string, string>>)this))
array[arrayIndex++] = item;
}
bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item) =>
((ICollection<NameValuePair>)this).Remove(new NameValuePair(item.Key, item.Value));
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
foreach (var nvp in this)
yield return new KeyValuePair<string, string>(nvp.Name, nvp.Value);
}
}
}

View File

@ -0,0 +1,363 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Win32
{
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,89 @@
using System.Linq;
namespace System.Security.AccessControl
{
/// <summary>Extensions for classes in the System.Security.AccessControl namespace.</summary>
public static class AccessControlExtension
{
/// <summary>Canonicalizes the specified Access Control List.</summary>
/// <param name="acl">The Access Control List.</param>
public static void Canonicalize(this RawAcl acl)
{
if (acl == null) throw new ArgumentNullException(nameof(acl));
// Extract aces to list
var aces = new Collections.Generic.List<GenericAce>(acl.Cast<GenericAce>());
// Sort aces based on canonical order
aces.Sort((a, b) => Collections.Generic.Comparer<byte>.Default.Compare(GetComparisonValue(a), GetComparisonValue(b)));
// Add sorted aces back to ACL
while (acl.Count > 0) acl.RemoveAce(0);
var aceIndex = 0;
aces.ForEach(ace => acl.InsertAce(aceIndex++, ace));
}
/// <summary>Sort ACEs according to canonical form for this <see cref="ObjectSecurity"/>.</summary>
/// <param name="objectSecurity">The object security whose DiscretionaryAcl will be made canonical.</param>
public static void CanonicalizeAccessRules(this ObjectSecurity objectSecurity)
{
if (objectSecurity == null) throw new ArgumentNullException(nameof(objectSecurity));
if (objectSecurity.AreAccessRulesCanonical) return;
// Get raw SD from objectSecurity and canonicalize DACL
var sd = new RawSecurityDescriptor(objectSecurity.GetSecurityDescriptorBinaryForm(), 0);
sd.DiscretionaryAcl.Canonicalize();
// Convert SD back into objectSecurity
objectSecurity.SetSecurityDescriptorBinaryForm(sd.GetBinaryForm());
}
/// <summary>Returns an array of byte values that represents the information contained in this <see cref="GenericSecurityDescriptor"/> object.</summary>
/// <param name="sd">The <see cref="GenericSecurityDescriptor"/> object.</param>
/// <returns>The byte array into which the contents of the <see cref="GenericSecurityDescriptor"/> is marshaled.</returns>
public static byte[] GetBinaryForm(this GenericSecurityDescriptor sd)
{
if (sd == null) throw new ArgumentNullException(nameof(sd));
var bin = new byte[sd.BinaryLength];
sd.GetBinaryForm(bin, 0);
return bin;
}
// A canonical ACL must have ACES sorted according to the following order:
// 1. Access-denied on the object
// 2. Access-denied on a child or property
// 3. Access-allowed on the object
// 4. Access-allowed on a child or property
// 5. All inherited ACEs
private static byte GetComparisonValue(GenericAce ace)
{
if ((ace.AceFlags & AceFlags.Inherited) != 0)
return 5;
switch (ace.AceType)
{
case AceType.AccessDenied:
case AceType.AccessDeniedCallback:
case AceType.SystemAudit:
case AceType.SystemAlarm:
case AceType.SystemAuditCallback:
case AceType.SystemAlarmCallback:
return 0;
case AceType.AccessDeniedObject:
case AceType.AccessDeniedCallbackObject:
case AceType.SystemAuditObject:
case AceType.SystemAlarmObject:
case AceType.SystemAuditCallbackObject:
case AceType.SystemAlarmCallbackObject:
return 1;
case AceType.AccessAllowed:
case AceType.AccessAllowedCallback:
return 2;
case AceType.AccessAllowedObject:
case AceType.AccessAllowedCallbackObject:
return 3;
default:
return 4;
}
}
}
}

View File

@ -0,0 +1,147 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
namespace System
{
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;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
#if NET20
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Attribute allowing extenders to be used with .NET Framework 2.0.
/// </summary>
internal sealed class ExtensionAttribute : Attribute
{
}
}
#endif

View File

@ -0,0 +1,32 @@
#if (NET20)
namespace System
{
/// <summary>Encapsulates a method that has one parameter and returns a value of the type specified by the TResult parameter.</summary>
/// <typeparam name="TResult">The type of the return value of the method that this delegate encapsulates.</typeparam>
/// <returns>The return value of the method that this delegate encapsulates.</returns>
internal delegate TResult Func<out TResult>();
/// <summary>Encapsulates a method that has one parameter and returns a value of the type specified by the TResult parameter.</summary>
/// <typeparam name="T">The type of the parameter of the method that this delegate encapsulates.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method that this delegate encapsulates.</typeparam>
/// <param name="arg">The parameter of the method that this delegate encapsulates.</param>
/// <returns>The return value of the method that this delegate encapsulates.</returns>
internal delegate TResult Func<in T, out TResult>(T arg);
/// <summary>Encapsulates a method that has two parameters and returns a value of the type specified by the TResult parameter.</summary>
/// <typeparam name="T1">The type of the first parameter of the method that this delegate encapsulates.</typeparam>
/// <typeparam name="T2">The type of the second parameter of the method that this delegate encapsulates.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method that this delegate encapsulates.</typeparam>
/// <param name="arg1">The first parameter of the method that this delegate encapsulates.</param>
/// <param name="arg2">The second parameter of the method that this delegate encapsulates.</param>
/// <returns>The return value of the method that this delegate encapsulates.</returns>
internal delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
/// <summary>Encapsulates a method that has two parameters and does not return a value.</summary>
/// <typeparam name="T1">The type of the first parameter of the method that this delegate encapsulates.</typeparam>
/// <typeparam name="T2">The type of the second parameter of the method that this delegate encapsulates.</typeparam>
/// <param name="arg1">The first parameter of the method that this delegate encapsulates.</param>
/// <param name="arg2">The second parameter of the method that this delegate encapsulates.</param>
internal delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
}
#endif

View File

@ -0,0 +1,143 @@
using System.Collections;
using System.Collections.Generic;
// ReSharper disable once CheckNamespace
namespace System.Runtime.InteropServices
{
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,59 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace Microsoft.Win32
{
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

@ -0,0 +1,338 @@
#if (NET20)
using System.Collections;
using System.Collections.Generic;
namespace System.Linq
{
/// <summary>Provides a set of static (Shared in Visual Basic) methods for querying objects that implement <see cref="IEnumerable{T}"/>.</summary>
internal static class Enumerable
{
/// <summary>Determines whether any element of a sequence satisfies a condition.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> whose elements to apply the predicate to.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns><c>true</c> if any elements in the source sequence pass the test in the specified predicate; otherwise, <c>false</c>.</returns>
public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
foreach (TSource element in source)
if (predicate(element)) return true;
return false;
}
/// <summary>Casts the elements of an <see cref="IEnumerable"/> to the specified type.</summary>
/// <typeparam name="TResult">The type to cast the elements of source to.</typeparam>
/// <param name="source">The <see cref="IEnumerable{T}"/> that contains the elements to be cast to type <typeparamref name="TResult"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> that contains each element of the source sequence cast to the specified type.</returns>
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
foreach (var i in source)
yield return (TResult)i;
}
/// <summary>Determines whether a sequence contains a specified element by using the default equality comparer.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence in which to locate a value.</param>
/// <param name="value">The value to locate in the sequence.</param>
/// <returns><c>true</c> if the source sequence contains an element that has the specified value; otherwise, <c>false</c>.</returns>
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value)
{
foreach (var i in source)
if (i.Equals(value)) return true;
return false;
}
/// <summary>Returns the number of elements in a sequence.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence that contains elements to be counted.</param>
/// <returns>The number of elements in the input sequence.</returns>
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (source is ICollection<TSource> c) return c.Count;
if (source is ICollection ngc) return ngc.Count;
var i = 0;
foreach (var e in source) i++;
return i;
}
/// <summary>Returns distinct elements from a sequence by using the default equality comparer to compare values.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">The sequence to remove duplicate elements from.</param>
/// <returns>An <see cref="IEnumerable{T}"/> that contains distinct elements from the source sequence.</returns>
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source)
{
var set = new Hashtable();
foreach (var element in source)
if (!set.ContainsKey(element))
{
set.Add(element, null);
yield return element;
}
}
/// <summary>Returns the first element of a sequence.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable{T}"/> to return the first element of.</param>
/// <returns>The first element in the specified sequence.</returns>
public static TSource First<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (source is IList<TSource> list)
{
if (list.Count > 0) return list[0];
}
else
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext()) return e.Current;
}
}
throw new InvalidOperationException(@"No elements");
}
/// <summary>Returns the first element of a sequence that satisfies a specified condition.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable{T}"/> to return the first element of.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns>The first element in the sequence that passes the test in the specified predicate function.</returns>
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
foreach (var element in source)
if (predicate(element)) return element;
throw new InvalidOperationException(@"No match");
}
/// <summary>Returns the first element of a sequence, or a default value if the sequence contains no elements.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable{T}"/> to return the first element of.</param>
/// <returns><c>default( <typeparamref name="TSource"/>)</c> if <paramref name="source"/> is empty; otherwise, the first element in <paramref name="source"/>.</returns>
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (source is IList<TSource> list)
{
if (list.Count > 0) return list[0];
}
else
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext()) return e.Current;
}
}
return default(TSource);
}
/// <summary>Returns the first element of the sequence that satisfies a condition or a default value if no such element is found.</summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> to return an element from.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns><c>default(<typeparamref name="TSource"/>)</c> if <paramref name="source"/> is empty or if no element passes the test specified by <paramref name="predicate"/>; otherwise, the first element in <paramref name="source"/> that passes the test specified by <paramref name="predicate"/>.</returns>
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
foreach (var element in source)
if (predicate(element)) return element;
return default(TSource);
}
/// <summary>Returns the minimum value in a generic sequence.</summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">A sequence of values to determine the minimum value of.</param>
/// <returns>The minimum value in the sequence.</returns>
public static TSource Min<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
var comparer = Comparer<TSource>.Default;
var value = default(TSource);
if (value == null)
{
foreach (var x in source)
{
if (x != null && (value == null || comparer.Compare(x, value) < 0))
value = x;
}
return value;
}
var hasValue = false;
foreach (var x in source)
{
if (hasValue)
{
if (comparer.Compare(x, value) < 0)
value = x;
}
else
{
value = x;
hasValue = true;
}
}
if (hasValue) return value;
throw new InvalidOperationException("No elements");
}
/// <summary>Returns the maximum value in a generic sequence.</summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">A sequence of values to determine the maximum value of.</param>
/// <returns>The maximum value in the sequence.</returns>
public static TSource Max<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
var comparer = Comparer<TSource>.Default;
var value = default(TSource);
if (value == null) {
foreach (var x in source) {
if (x != null && (value == null || comparer.Compare(x, value) > 0))
value = x;
}
return value;
}
var hasValue = false;
foreach (var x in source) {
if (hasValue) {
if (comparer.Compare(x, value) > 0)
value = x;
}
else {
value = x;
hasValue = true;
}
}
if (hasValue) return value;
throw new InvalidOperationException("No elements");
}
/// <summary>Sorts the elements of a sequence in ascending order according to a key.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="source">A sequence of values to order.</param>
/// <param name="keySelector">A function to extract a key from an element.</param>
/// <returns>An <see cref="IEnumerable{T}"/> whose elements are sorted according to a key.</returns>
public static IEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var d = new SortedDictionary<TKey, TSource>();
foreach (var item in source)
d.Add(keySelector(item), item);
return d.Values;
}
/// <summary>Projects each element of a sequence into a new form.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the value returned by <paramref name="selector"/>.</typeparam>
/// <param name="source">A sequence of values to invoke a transform function on.</param>
/// <param name="selector">A transform function to apply to each element.</param>
/// <returns>An <see cref="IEnumerable{T}"/> whose elements are the result of invoking the transform function on each element of <paramref name="source"/>.</returns>
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
if (selector == null) throw new ArgumentNullException(nameof(selector));
foreach (var i in source)
yield return selector(i);
}
/// <summary>Returns the only element of a sequence that satisfies a specified condition, and throws an exception if more than one such element exists.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> to return a single element from.</param>
/// <param name="predicate">A function to test an element for a condition.</param>
/// <returns>The single element of the input sequence that satisfies a condition.</returns>
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
var result = default(TSource);
long count = 0;
foreach (var element in source)
{
if (!predicate(element)) continue;
result = element;
checked { count++; }
}
if (count == 0) throw new InvalidOperationException(@"No matches");
if (count != 1) throw new InvalidOperationException(@"More than one match.");
return result;
}
/// <summary>Computes the sum of a sequence of nullable <see cref="Int32"/> values.</summary>
/// <param name="source">A sequence of nullable <see cref="Int32"/> values to calculate the sum of.</param>
/// <returns>The sum of the values in the sequence.</returns>
public static int Sum(this IEnumerable<int> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
int sum = 0;
checked
{
foreach (int v in source) sum += v;
}
return sum;
}
/// <summary>
/// Computes the sum of the sequence of nullable <see cref="Int32"/> values that are obtained by invoking a transform function on each element of the input sequence.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">A sequence of values that are used to calculate a sum.</param>
/// <param name="selector">A transform function to apply to each element.</param>
/// <returns>The sum of the projected values.</returns>
public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector) => Sum(Select(source, selector));
/// <summary>Creates an array from a <see cref="IEnumerable"/>.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> to create an array from.</param>
/// <returns>An array that contains the elements from the input sequence.</returns>
public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source) => ToList(source).ToArray();
/// <summary>
/// Creates a <see cref="Dictionary{TKey,TValue}"/> from an <see cref="IEnumerable{T}"/> according to a specified key selector function, a comparer, and
/// an element selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <typeparam name="TElement">The type of the value returned by <paramref name="elementSelector"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> to create a <see cref="Dictionary{TKey,TValue}"/> from.</param>
/// <param name="keySelector">A function to extract a key from each element.</param>
/// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
/// <param name="comparer">An <see cref="IEqualityComparer{T}"/> to compare keys.</param>
/// <returns>A <see cref="Dictionary{TKey,TValue}"/> that contains values of type TElement selected from the input sequence.</returns>
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector));
var d = new Dictionary<TKey, TElement>(comparer);
foreach (var element in source) d.Add(keySelector(element), elementSelector(element));
return d;
}
/// <summary>Creates a <see cref="List{T}"/> from an <see cref="IEnumerable{T}"/>.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> to create a <see cref="List{T}"/> from.</param>
/// <returns>A <see cref="List{T}"/> that contains elements from the input sequence.</returns>
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
var l = new List<TSource>();
foreach (var i in source)
l.Add(i);
return l;
}
/// <summary>Filters a sequence of values based on a predicate.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}"/> to filter.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <returns>An <see cref="IEnumerable{T}"/> that contains elements from the input sequence that satisfy the condition.</returns>
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
foreach (var i in source)
if (predicate(i)) yield return i;
}
}
}
#endif

View File

@ -0,0 +1,230 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
namespace Microsoft.Win32
{
internal static partial class NativeMethods
{
private const string NTDSAPI = "ntdsapi.dll";
/// <summary>
/// Defines the errors returned by the status member of the DS_NAME_RESULT_ITEM structure. These are potential errors that may be encountered while a name is converted by the DsCrackNames function.
/// </summary>
public enum DS_NAME_ERROR : uint
{
/// <summary>The conversion was successful.</summary>
DS_NAME_NO_ERROR = 0,
///<summary>Generic processing error occurred.</summary>
DS_NAME_ERROR_RESOLVING = 1,
///<summary>The name cannot be found or the caller does not have permission to access the name.</summary>
DS_NAME_ERROR_NOT_FOUND = 2,
///<summary>The input name is mapped to more than one output name or the desired format did not have a single, unique value for the object found.</summary>
DS_NAME_ERROR_NOT_UNIQUE = 3,
///<summary>The input name was found, but the associated output format cannot be found. This can occur if the object does not have all the required attributes.</summary>
DS_NAME_ERROR_NO_MAPPING = 4,
///<summary>Unable to resolve entire name, but was able to determine in which domain object resides. The caller is expected to retry the call at a domain controller for the specified domain. The entire name cannot be resolved, but the domain that the object resides in could be determined. The pDomain member of the DS_NAME_RESULT_ITEM contains valid data when this error is specified.</summary>
DS_NAME_ERROR_DOMAIN_ONLY = 5,
///<summary>A syntactical mapping cannot be performed on the client without transmitting over the network.</summary>
DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 6,
///<summary>The name is from an external trusted forest.</summary>
DS_NAME_ERROR_TRUST_REFERRAL = 7
}
/// <summary>
/// Used to define how the name syntax will be cracked. These flags are used by the DsCrackNames function.
/// </summary>
[Flags]
public enum DS_NAME_FLAGS
{
/// <summary>Indicate that there are no associated flags.</summary>
DS_NAME_NO_FLAGS = 0x0,
///<summary>Perform a syntactical mapping at the client without transferring over the network. The only syntactic mapping supported is from DS_FQDN_1779_NAME to DS_CANONICAL_NAME or DS_CANONICAL_NAME_EX.</summary>
DS_NAME_FLAG_SYNTACTICAL_ONLY = 0x1,
///<summary>Force a trip to the DC for evaluation, even if this could be locally cracked syntactically.</summary>
DS_NAME_FLAG_EVAL_AT_DC = 0x2,
///<summary>The call fails if the domain controller is not a global catalog server.</summary>
DS_NAME_FLAG_GCVERIFY = 0x4,
///<summary>Enable cross forest trust referral.</summary>
DS_NAME_FLAG_TRUST_REFERRAL = 0x8
}
/// <summary>
/// Provides formats to use for input and output names for the DsCrackNames function.
/// </summary>
public enum DS_NAME_FORMAT
{
///<summary>Indicates the name is using an unknown name type. This format can impact performance because it forces the server to attempt to match all possible formats. Only use this value if the input format is unknown.</summary>
DS_UNKNOWN_NAME = 0,
///<summary>Indicates that the fully qualified distinguished name is used. For example: "CN = someone, OU = Users, DC = Engineering, DC = Fabrikam, DC = Com"</summary>
DS_FQDN_1779_NAME = 1,
///<summary>Indicates a Windows NT 4.0 account name. For example: "Engineering\someone" The domain-only version includes two trailing backslashes (\\).</summary>
DS_NT4_ACCOUNT_NAME = 2,
///<summary>Indicates a user-friendly display name, for example, Jeff Smith. The display name is not necessarily the same as relative distinguished name (RDN).</summary>
DS_DISPLAY_NAME = 3,
///<summary>Indicates a GUID string that the IIDFromString function returns. For example: "{4fa050f0-f561-11cf-bdd9-00aa003a77b6}"</summary>
DS_UNIQUE_ID_NAME = 6,
///<summary>Indicates a complete canonical name. For example: "engineering.fabrikam.com/software/someone" The domain-only version includes a trailing forward slash (/).</summary>
DS_CANONICAL_NAME = 7,
///<summary>Indicates that it is using the user principal name (UPN). For example: "someone@engineering.fabrikam.com"</summary>
DS_USER_PRINCIPAL_NAME = 8,
///<summary>This element is the same as DS_CANONICAL_NAME except that the rightmost forward slash (/) is replaced with a newline character (\n), even in a domain-only case. For example: "engineering.fabrikam.com/software\nsomeone"</summary>
DS_CANONICAL_NAME_EX = 9,
///<summary>Indicates it is using a generalized service principal name. For example: "www/www.fabrikam.com@fabrikam.com"</summary>
DS_SERVICE_PRINCIPAL_NAME = 10,
///<summary>Indicates a Security Identifier (SID) for the object. This can be either the current SID or a SID from the object SID history. The SID string can use either the standard string representation of a SID, or one of the string constants defined in Sddl.h. For more information about converting a binary SID into a SID string, see SID Strings. The following is an example of a SID string: "S-1-5-21-397955417-626881126-188441444-501"</summary>
DS_SID_OR_SID_HISTORY_NAME = 11,
}
/// <summary>
/// Class that provides methods against a AD domain service.
/// </summary>
/// <seealso cref="System.IDisposable" />
[SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public class DomainService : IDisposable
{
IntPtr handle = IntPtr.Zero;
/// <summary>
/// Initializes a new instance of the <see cref="DomainService"/> class.
/// </summary>
/// <param name="domainControllerName">Name of the domain controller.</param>
/// <param name="dnsDomainName">Name of the DNS domain.</param>
/// <exception cref="System.ComponentModel.Win32Exception"></exception>
public DomainService(string domainControllerName = null, string dnsDomainName = null)
{
DsBind(domainControllerName, dnsDomainName, out handle);
}
/// <summary>
/// Converts a directory service object name from any format to the UPN.
/// </summary>
/// <param name="name">The name to convert.</param>
/// <returns>The corresponding UPN.</returns>
/// <exception cref="System.Security.SecurityException">Unable to resolve user name.</exception>
public string CrackName(string name)
{
var res = CrackNames(new string[] { name });
if (res == null || res.Length == 0 || res[0].status != NativeMethods.DS_NAME_ERROR.DS_NAME_NO_ERROR)
throw new SecurityException("Unable to resolve user name.");
return res[0].pName;
}
/// <summary>
/// Converts an array of directory service object names from one format to another. Name conversion enables client applications to map between the multiple names used to identify various directory service objects.
/// </summary>
/// <param name="names">The names to convert.</param>
/// <param name="flags">Values used to determine how the name syntax will be cracked.</param>
/// <param name="formatOffered">Format of the input names.</param>
/// <param name="formatDesired">Desired format for the output names.</param>
/// <returns>An array of DS_NAME_RESULT_ITEM structures. Each element of this array represents a single converted name.</returns>
public DS_NAME_RESULT_ITEM[] CrackNames(string[] names = null, DS_NAME_FLAGS flags = DS_NAME_FLAGS.DS_NAME_NO_FLAGS, DS_NAME_FORMAT formatOffered = DS_NAME_FORMAT.DS_UNKNOWN_NAME, DS_NAME_FORMAT formatDesired = DS_NAME_FORMAT.DS_USER_PRINCIPAL_NAME)
{
IntPtr pResult;
uint err = DsCrackNames(handle, flags, formatOffered, formatDesired, (uint)(names?.Length ?? 0), names, out pResult);
if (err != (uint)DS_NAME_ERROR.DS_NAME_NO_ERROR)
throw new System.ComponentModel.Win32Exception((int)err);
try
{
// Next convert the returned structure to managed environment
DS_NAME_RESULT Result = (DS_NAME_RESULT)Marshal.PtrToStructure(pResult, typeof(DS_NAME_RESULT));
return Result.Items;
}
finally
{
DsFreeNameResult(pResult);
}
}
public void Dispose()
{
uint ret = DsUnBind(ref handle);
System.Diagnostics.Debug.WriteLineIf(ret != 0, "Error unbinding :\t" + ret.ToString());
}
}
[DllImport(NTDSAPI, CharSet = CharSet.Auto, PreserveSig = false)]
public static extern void DsBind(
string DomainControllerName, // in, optional
string DnsDomainName, // in, optional
out IntPtr phDS);
[DllImport(NTDSAPI, CharSet = CharSet.Auto)]
public static extern uint DsCrackNames(
IntPtr hDS,
DS_NAME_FLAGS flags,
DS_NAME_FORMAT formatOffered,
DS_NAME_FORMAT formatDesired,
uint cNames,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPTStr, SizeParamIndex = 4)] string[] rpNames,
out IntPtr ppResult);
[DllImport(NTDSAPI, CharSet = CharSet.Auto)]
public static extern void DsFreeNameResult(IntPtr pResult /* DS_NAME_RESULT* */);
[DllImport(NTDSAPI, CharSet = CharSet.Auto)]
public static extern uint DsUnBind(ref IntPtr phDS);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DS_NAME_RESULT
{
public uint cItems;
internal IntPtr rItems; // PDS_NAME_RESULT_ITEM
public DS_NAME_RESULT_ITEM[] Items
{
get
{
if (rItems == IntPtr.Zero)
return new DS_NAME_RESULT_ITEM[0];
var ResultArray = new DS_NAME_RESULT_ITEM[cItems];
Type strType = typeof(DS_NAME_RESULT_ITEM);
int stSize = Marshal.SizeOf(strType);
IntPtr curptr;
for (uint i = 0; i < cItems; i++)
{
curptr = new IntPtr(rItems.ToInt64() + (i * stSize));
ResultArray[i] = (DS_NAME_RESULT_ITEM)Marshal.PtrToStructure(curptr, strType);
}
return ResultArray;
}
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DS_NAME_RESULT_ITEM
{
public DS_NAME_ERROR status;
public string pDomain;
public string pName;
public override string ToString()
{
if (status == DS_NAME_ERROR.DS_NAME_NO_ERROR)
return pName;
return string.Empty;
}
}
}
}

View File

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
namespace Microsoft.Win32
{
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

@ -0,0 +1,116 @@
using System;
using System.Runtime.InteropServices;
namespace Microsoft.Win32
{
internal static partial class NativeMethods
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal struct SYSTEMTIME : IConvertible
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Milliseconds;
public SYSTEMTIME(DateTime dt)
{
dt = dt.ToLocalTime();
Year = Convert.ToUInt16(dt.Year);
Month = Convert.ToUInt16(dt.Month);
DayOfWeek = Convert.ToUInt16(dt.DayOfWeek);
Day = Convert.ToUInt16(dt.Day);
Hour = Convert.ToUInt16(dt.Hour);
Minute = Convert.ToUInt16(dt.Minute);
Second = Convert.ToUInt16(dt.Second);
Milliseconds = Convert.ToUInt16(dt.Millisecond);
}
public SYSTEMTIME(ushort year, ushort month, ushort day, ushort hour = 0, ushort minute = 0, ushort second = 0, ushort millisecond = 0)
{
Year = year;
Month = month;
Day = day;
Hour = hour;
Minute = minute;
Second = second;
Milliseconds = millisecond;
DayOfWeek = 0;
}
public static implicit operator DateTime(SYSTEMTIME st)
{
if (st.Year == 0 || st == MinValue)
return DateTime.MinValue;
if (st == MaxValue)
return DateTime.MaxValue;
return new DateTime(st.Year, st.Month, st.Day, st.Hour, st.Minute, st.Second, st.Milliseconds, DateTimeKind.Local);
}
public static implicit operator SYSTEMTIME(DateTime dt) => new SYSTEMTIME(dt);
public static bool operator ==(SYSTEMTIME s1, SYSTEMTIME s2) => (s1.Year == s2.Year && s1.Month == s2.Month && s1.Day == s2.Day && s1.Hour == s2.Hour && s1.Minute == s2.Minute && s1.Second == s2.Second && s1.Milliseconds == s2.Milliseconds);
public static bool operator !=(SYSTEMTIME s1, SYSTEMTIME s2) => !(s1 == s2);
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)
{
if (obj is SYSTEMTIME)
return ((SYSTEMTIME)obj) == this;
if (obj is DateTime)
return ((DateTime)this).Equals(obj);
return base.Equals(obj);
}
public override int GetHashCode() => ((DateTime)this).GetHashCode();
public override string ToString() => ((DateTime)this).ToString();
TypeCode IConvertible.GetTypeCode() => ((IConvertible)(DateTime)this).GetTypeCode();
bool IConvertible.ToBoolean(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToBoolean(provider);
byte IConvertible.ToByte(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToByte(provider);
char IConvertible.ToChar(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToChar(provider);
DateTime IConvertible.ToDateTime(IFormatProvider provider) => (DateTime)this;
decimal IConvertible.ToDecimal(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToDecimal(provider);
double IConvertible.ToDouble(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToDouble(provider);
short IConvertible.ToInt16(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt16(provider);
int IConvertible.ToInt32(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt32(provider);
long IConvertible.ToInt64(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToInt64(provider);
sbyte IConvertible.ToSByte(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToSByte(provider);
float IConvertible.ToSingle(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToSingle(provider);
string IConvertible.ToString(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToString(provider);
object IConvertible.ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)(DateTime)this).ToType(conversionType, provider);
ushort IConvertible.ToUInt16(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt16(provider);
uint IConvertible.ToUInt32(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt32(provider);
ulong IConvertible.ToUInt64(IFormatProvider provider) => ((IConvertible)(DateTime)this).ToUInt64(provider);
}
}
}

View File

@ -0,0 +1,25 @@
#if (NET20 || NET35 || NET40)
namespace System.Collections.Generic
{
/// <summary>Represents a strongly-typed, read-only collection of elements.</summary>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <seealso cref="System.Collections.Generic.IEnumerable{T}"/>
public interface IReadOnlyCollection<T> : IEnumerable<T>
{
/// <summary>Gets the number of elements in the collection.</summary>
/// <value>The number of elements in the collection.</value>
int Count { get; }
}
/// <summary>Represents a read-only collection of elements that can be accessed by index.</summary>
/// <typeparam name="T">The type of elements in the read-only list.</typeparam>
/// <seealso cref="System.Collections.Generic.IReadOnlyCollection{T}"/>
public interface IReadOnlyList<T> : IReadOnlyCollection<T>
{
/// <summary>Gets the element at the specified index in the read-only list.</summary>
/// <value>The element at the specified index in the read-only list.</value>
/// <param name="index">The zero-based index of the element to get.</param>
T this[int index] { get; }
}
}
#endif

View File

@ -0,0 +1,142 @@
#if (NET20 || NET35)
namespace System.Collections.Specialized
{
/// <summary>
/// Stub
/// </summary>
public interface INotifyCollectionChanged
{
/// <summary>
/// Stub
/// </summary>
event NotifyCollectionChangedEventHandler CollectionChanged;
}
/// <summary>
/// Stub
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs e);
/// <summary>
/// Stub
/// </summary>
public class NotifyCollectionChangedEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="NotifyCollectionChangedEventArgs"/> class.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="newItems">The new items.</param>
/// <param name="oldItems">The old items.</param>
/// <param name="newIndex">The new index.</param>
/// <param name="oldIndex">The old index.</param>
internal NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems, int newIndex, int oldIndex)
{
Action = action;
NewItems = newItems;
NewStartingIndex = newIndex;
OldItems = oldItems;
OldStartingIndex = oldIndex;
}
/// <summary>
/// Initializes a new instance of the <see cref="NotifyCollectionChangedEventArgs"/> class.
/// </summary>
/// <param name="action">The action.</param>
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action) :
this(action, null, null, -1, -1) { }
/// <summary>
/// Initializes a new instance of the <see cref="NotifyCollectionChangedEventArgs"/> class.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="item">The item.</param>
/// <param name="idx">The index.</param>
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object item, int idx = -1) :
this(action, new object[] { item }, null, idx, -1) { }
/// <summary>
/// Initializes a new instance of the <see cref="NotifyCollectionChangedEventArgs"/> class.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="item">The item.</param>
/// <param name="item2">The item2.</param>
/// <param name="idx">The index.</param>
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object item, object item2, int idx) :
this(action, new object[] { item }, new object[] { item2 }, idx, -1) { }
/// <summary>
/// Initializes a new instance of the <see cref="NotifyCollectionChangedEventArgs"/> class.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="newItems">The new items.</param>
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems) :
this(action, newItems, null, -1, -1) { }
/// <summary>
/// Gets the action.
/// </summary>
/// <value>
/// The action.
/// </value>
public NotifyCollectionChangedAction Action { get; }
/// <summary>
/// Gets the new items.
/// </summary>
/// <value>
/// The new items.
/// </value>
public IList NewItems { get; }
/// <summary>
/// Gets the new index of the starting.
/// </summary>
/// <value>
/// The new index of the starting.
/// </value>
public int NewStartingIndex { get; }
/// <summary>
/// Gets the old items.
/// </summary>
/// <value>
/// The old items.
/// </value>
public IList OldItems { get; }
/// <summary>
/// Gets the old index of the starting.
/// </summary>
/// <value>
/// The old index of the starting.
/// </value>
public int OldStartingIndex { get; }
}
/// <summary>
/// Stub
/// </summary>
public enum NotifyCollectionChangedAction
{
/// <summary>
/// The add
/// </summary>
Add = 0,
/// <summary>
/// The move
/// </summary>
Move = 3,
/// <summary>
/// The remove
/// </summary>
Remove = 1,
/// <summary>
/// The replace
/// </summary>
Replace = 2,
/// <summary>
/// The reset
/// </summary>
Reset = 4
}
}
#endif

View File

@ -0,0 +1,27 @@
#if (NET20 || NET35 || NET40)
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Allows you to obtain the full path of the source file that contains the caller. This is the file path at the time of compile.
/// </summary>
/// <seealso cref="System.Attribute"/>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class CallerFilePathAttribute : Attribute
{
}
/// <summary>Allows you to obtain the line number in the source file at which the method is called.</summary>
/// <seealso cref="System.Attribute"/>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class CallerLineNumberAttribute : Attribute
{
}
/// <summary>Allows you to obtain the method or property name of the caller to the method.</summary>
/// <seealso cref="System.Attribute"/>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class CallerMemberNameAttribute : Attribute
{
}
}
#endif

View File

@ -0,0 +1,810 @@
#if (NET20 || NET35)
#pragma warning disable 0420
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// ThreadLocal.cs
//
// <OWNER>Microsoft</OWNER>
//
// A class that provides a simple, lightweight implementation of thread-local lazy-initialization, where a value is initialized once per accessing
// thread; this provides an alternative to using a ThreadStatic static variable and having
// to check the variable prior to every access to see if it's been initialized.
//
//
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
using System.Diagnostics;
using System.Collections.Generic;
using System.Security.Permissions;
namespace System.Threading
{
/// <summary>
/// Provides thread-local storage of data.
/// </summary>
/// <typeparam name="T">Specifies the type of data stored per-thread.</typeparam>
/// <remarks>
/// <para>
/// With the exception of <see cref="Dispose()"/>, all public and protected members of
/// <see cref="ThreadLocal{T}"/> are thread-safe and may be used
/// concurrently from multiple threads.
/// </para>
/// </remarks>
[DebuggerTypeProxy(typeof(SystemThreading_ThreadLocalDebugView<>))]
[DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}, Count={ValuesCountForDebugDisplay}")]
[HostProtection(Synchronization = true, ExternalThreading = true)]
internal class ThreadLocal<T> : IDisposable
{
private const int MaxArrayLength = 0X7FEFFFFF;
// a delegate that returns the created value, if null the created value will be default(T)
private Func<T> m_valueFactory;
//
// ts_slotArray is a table of thread-local values for all ThreadLocal<T> instances
//
// So, when a thread reads ts_slotArray, it gets back an array of *all* ThreadLocal<T> values for this thread and this T.
// The slot relevant to this particular ThreadLocal<T> instance is determined by the m_idComplement instance field stored in
// the ThreadLocal<T> instance.
//
[ThreadStatic]
static LinkedSlotVolatile[] ts_slotArray;
[ThreadStatic]
static FinalizationHelper ts_finalizationHelper;
// Slot ID of this ThreadLocal<> instance. We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish
// between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or
// possibly due to a memory model issue in user code.
private int m_idComplement;
// This field is set to true when the constructor completes. That is helpful for recognizing whether a constructor
// threw an exception - either due to invalid argument or due to a thread abort. Finally, the field is set to false
// when the instance is disposed.
private volatile bool m_initialized;
// IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock.
private static IdManager s_idManager = new IdManager();
// A linked list of all values associated with this ThreadLocal<T> instance.
// We create a dummy head node. That allows us to remove any (non-dummy) node without having to locate the m_linkedSlot field.
private LinkedSlot m_linkedSlot = new LinkedSlot(null);
// Whether the Values property is supported
private bool m_trackAllValues;
/// <summary>
/// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
/// </summary>
public ThreadLocal()
{
Initialize(null, false);
}
/// <summary>
/// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
/// </summary>
/// <param name="trackAllValues">Whether to track all values set on the instance and expose them through the Values property.</param>
public ThreadLocal(bool trackAllValues)
{
Initialize(null, trackAllValues);
}
/// <summary>
/// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
/// specified <paramref name="valueFactory"/> function.
/// </summary>
/// <param name="valueFactory">
/// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when
/// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
/// </param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
/// </exception>
public ThreadLocal(Func<T> valueFactory)
{
if (valueFactory == null)
throw new ArgumentNullException("valueFactory");
Initialize(valueFactory, false);
}
/// <summary>
/// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
/// specified <paramref name="valueFactory"/> function.
/// </summary>
/// <param name="valueFactory">
/// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when
/// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
/// </param>
/// <param name="trackAllValues">Whether to track all values set on the instance and expose them via the Values property.</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
/// </exception>
public ThreadLocal(Func<T> valueFactory, bool trackAllValues)
{
if (valueFactory == null)
throw new ArgumentNullException("valueFactory");
Initialize(valueFactory, trackAllValues);
}
private void Initialize(Func<T> valueFactory, bool trackAllValues)
{
m_valueFactory = valueFactory;
m_trackAllValues = trackAllValues;
// Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set m_initialized
// in a finally block, to avoid a thread abort in between the two statements.
try { }
finally
{
m_idComplement = ~s_idManager.GetId();
// As the last step, mark the instance as fully initialized. (Otherwise, if m_initialized=false, we know that an exception
// occurred in the constructor.)
m_initialized = true;
}
}
/// <summary>
/// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
/// </summary>
~ThreadLocal()
{
// finalizer to return the type combination index to the pool
Dispose(false);
}
#region IDisposable Members
/// <summary>
/// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
/// </summary>
/// <remarks>
/// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
/// </remarks>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
/// </summary>
/// <param name="disposing">
/// A Boolean value that indicates whether this method is being called due to a call to <see cref="Dispose()"/>.
/// </param>
/// <remarks>
/// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
/// </remarks>
protected virtual void Dispose(bool disposing)
{
int id;
lock (s_idManager)
{
id = ~m_idComplement;
m_idComplement = 0;
if (id < 0 || !m_initialized)
{
// Handle double Dispose calls or disposal of an instance whose constructor threw an exception.
return;
}
m_initialized = false;
for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
{
LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray;
if (slotArray == null)
{
// The thread that owns this slotArray has already finished.
continue;
}
// Remove the reference from the LinkedSlot to the slot table.
linkedSlot.SlotArray = null;
// And clear the references from the slot table to the linked slot and the value so that
// both can get garbage collected.
slotArray[id].Value.Value = default(T);
slotArray[id].Value = null;
}
}
m_linkedSlot = null;
s_idManager.ReturnId(id);
}
#endregion
/// <summary>Creates and returns a string representation of this instance for the current thread.</summary>
/// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see cref="Value"/>.</returns>
/// <exception cref="T:System.NullReferenceException">
/// The <see cref="Value"/> for the current thread is a null reference (Nothing in Visual Basic).
/// </exception>
/// <exception cref="T:System.InvalidOperationException">
/// The initialization function referenced <see cref="Value"/> in an improper manner.
/// </exception>
/// <exception cref="T:System.ObjectDisposedException">
/// The <see cref="ThreadLocal{T}"/> instance has been disposed.
/// </exception>
/// <remarks>
/// Calling this method forces initialization for the current thread, as is the
/// case with accessing <see cref="Value"/> directly.
/// </remarks>
public override string ToString()
{
return Value.ToString();
}
/// <summary>
/// Gets or sets the value of this instance for the current thread.
/// </summary>
/// <exception cref="T:System.InvalidOperationException">
/// The initialization function referenced <see cref="Value"/> in an improper manner.
/// </exception>
/// <exception cref="T:System.ObjectDisposedException">
/// The <see cref="ThreadLocal{T}"/> instance has been disposed.
/// </exception>
/// <remarks>
/// If this instance was not previously initialized for the current thread,
/// accessing <see cref="Value"/> will attempt to initialize it. If an initialization function was
/// supplied during the construction, that initialization will happen by invoking the function
/// to retrieve the initial value for <see cref="Value"/>. Otherwise, the default value of
/// <typeparamref name="T"/> will be used.
/// </remarks>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public T Value
{
get
{
LinkedSlotVolatile[] slotArray = ts_slotArray;
LinkedSlot slot;
int id = ~m_idComplement;
//
// Attempt to get the value using the fast path
//
if (slotArray != null // Has the slot array been initialized?
&& id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
&& id < slotArray.Length // Is the table large enough?
&& (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
&& m_initialized // Has the instance *still* not been disposed (important for ----s with Dispose)?
)
{
// We verified that the instance has not been disposed *after* we got a reference to the slot.
// This guarantees that we have a reference to the right slot.
//
// Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
// will not be reordered before the read of slotArray[id].
return slot.Value;
}
return GetValueSlow();
}
set
{
LinkedSlotVolatile[] slotArray = ts_slotArray;
LinkedSlot slot;
int id = ~m_idComplement;
//
// Attempt to set the value using the fast path
//
if (slotArray != null // Has the slot array been initialized?
&& id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
&& id < slotArray.Length // Is the table large enough?
&& (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
&& m_initialized // Has the instance *still* not been disposed (important for ----s with Dispose)?
)
{
// We verified that the instance has not been disposed *after* we got a reference to the slot.
// This guarantees that we have a reference to the right slot.
//
// Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
// will not be reordered before the read of slotArray[id].
slot.Value = value;
}
else
{
SetValueSlow(value, slotArray);
}
}
}
private T GetValueSlow()
{
// If the object has been disposed, the id will be -1.
int id = ~m_idComplement;
if (id < 0)
{
throw new ObjectDisposedException("The ThreadLocal<T> instance has been disposed.");
}
// Determine the initial value
T value;
if (m_valueFactory == null)
{
value = default(T);
}
else
{
value = m_valueFactory();
if (IsValueCreated)
{
throw new InvalidOperationException("The initialization function attempted to reference Value recursively.");
}
}
// Since the value has been previously uninitialized, we also need to set it (according to the ThreadLocal semantics).
Value = value;
return value;
}
private void SetValueSlow(T value, LinkedSlotVolatile[] slotArray)
{
int id = ~m_idComplement;
// If the object has been disposed, id will be -1.
if (id < 0)
{
throw new ObjectDisposedException("The ThreadLocal<T> instance has been disposed.");
}
// If a slot array has not been created on this thread yet, create it.
if (slotArray == null)
{
slotArray = new LinkedSlotVolatile[GetNewTableSize(id + 1)];
ts_finalizationHelper = new FinalizationHelper(slotArray, m_trackAllValues);
ts_slotArray = slotArray;
}
// If the slot array is not big enough to hold this ID, increase the table size.
if (id >= slotArray.Length)
{
GrowTable(ref slotArray, id + 1);
ts_finalizationHelper.SlotArray = slotArray;
ts_slotArray = slotArray;
}
// If we are using the slot in this table for the first time, create a new LinkedSlot and add it into
// the linked list for this ThreadLocal instance.
if (slotArray[id].Value == null)
{
CreateLinkedSlot(slotArray, id, value);
}
else
{
// Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
// that follows will not be reordered before the read of slotArray[id].
LinkedSlot slot = slotArray[id].Value;
// It is important to verify that the ThreadLocal instance has not been disposed. The check must come
// after capturing slotArray[id], but before assigning the value into the slot. This ensures that
// if this ThreadLocal instance was disposed on another thread and another ThreadLocal instance was
// created, we definitely won't assign the value into the wrong instance.
if (!m_initialized)
{
throw new ObjectDisposedException("The ThreadLocal<T> instance has been disposed.");
}
slot.Value = value;
}
}
/// <summary>
/// Creates a LinkedSlot and inserts it into the linked list for this ThreadLocal instance.
/// </summary>
private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value)
{
// Create a LinkedSlot
var linkedSlot = new LinkedSlot(slotArray);
// Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array
lock (s_idManager)
{
// Check that the instance has not been disposed. It is important to check this under a lock, since
// Dispose also executes under a lock.
if (!m_initialized)
{
throw new ObjectDisposedException("The ThreadLocal<T> instance has been disposed.");
}
LinkedSlot firstRealNode = m_linkedSlot.Next;
//
// Insert linkedSlot between nodes m_linkedSlot and firstRealNode.
// (m_linkedSlot is the dummy head node that should always be in the front.)
//
linkedSlot.Next = firstRealNode;
linkedSlot.Previous = m_linkedSlot;
linkedSlot.Value = value;
if (firstRealNode != null)
{
firstRealNode.Previous = linkedSlot;
}
m_linkedSlot.Next = linkedSlot;
// Assigning the slot under a lock prevents a ---- with Dispose (dispose also acquires the lock).
// Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created
// with the same ID, and the write would go to the wrong instance.
slotArray[id].Value = linkedSlot;
}
}
/// <summary>
/// Gets a list for all of the values currently stored by all of the threads that have accessed this instance.
/// </summary>
/// <exception cref="T:System.ObjectDisposedException">
/// The <see cref="ThreadLocal{T}"/> instance has been disposed.
/// </exception>
public IList<T> Values
{
get
{
if (!m_trackAllValues)
{
throw new InvalidOperationException("Values stored by all threads are not available because this instance was initialized with the trackAllValues argument set to false in the call to a class constructor.");
}
var list = GetValuesAsList(); // returns null if disposed
if (list == null) throw new ObjectDisposedException("The ThreadLocal<T> instance has been disposed.");
return list;
}
}
/// <summary>Gets all of the threads' values in a list.</summary>
private List<T> GetValuesAsList()
{
List<T> valueList = new List<T>();
int id = ~m_idComplement;
if (id == -1)
{
return null;
}
// Walk over the linked list of slots and gather the values associated with this ThreadLocal instance.
for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
{
// We can safely read linkedSlot.Value. Even if this ThreadLocal has been disposed in the meantime, the LinkedSlot
// objects will never be assigned to another ThreadLocal instance.
valueList.Add(linkedSlot.Value);
}
return valueList;
}
/// <summary>Gets the number of threads that have data in this instance.</summary>
private int ValuesCountForDebugDisplay
{
get
{
int count = 0;
for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
{
count++;
}
return count;
}
}
/// <summary>
/// Gets whether <see cref="Value"/> is initialized on the current thread.
/// </summary>
/// <exception cref="T:System.ObjectDisposedException">
/// The <see cref="ThreadLocal{T}"/> instance has been disposed.
/// </exception>
public bool IsValueCreated
{
get
{
int id = ~m_idComplement;
if (id < 0)
{
throw new ObjectDisposedException("The ThreadLocal<T> instance has been disposed.");
}
LinkedSlotVolatile[] slotArray = ts_slotArray;
return slotArray != null && id < slotArray.Length && slotArray[id].Value != null;
}
}
/// <summary>Gets the value of the ThreadLocal&lt;T&gt; for debugging display purposes. It takes care of getting
/// the value for the current thread in the ThreadLocal mode.</summary>
internal T ValueForDebugDisplay
{
get
{
LinkedSlotVolatile[] slotArray = ts_slotArray;
int id = ~m_idComplement;
LinkedSlot slot;
if (slotArray == null || id >= slotArray.Length || (slot = slotArray[id].Value) == null || !m_initialized)
return default(T);
return slot.Value;
}
}
/// <summary>Gets the values of all threads that accessed the ThreadLocal&lt;T&gt;.</summary>
internal List<T> ValuesForDebugDisplay // same as Values property, but doesn't throw if disposed
{
get { return GetValuesAsList(); }
}
/// <summary>
/// Resizes a table to a certain length (or larger).
/// </summary>
private void GrowTable(ref LinkedSlotVolatile[] table, int minLength)
{
// Determine the size of the new table and allocate it.
int newLen = GetNewTableSize(minLength);
LinkedSlotVolatile[] newTable = new LinkedSlotVolatile[newLen];
//
// The lock is necessary to avoid a race with ThreadLocal.Dispose. GrowTable has to point all
// LinkedSlot instances referenced in the old table to reference the new table. Without locking,
// Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while
// the value continues to be referenced from the new (larger) array.
//
lock (s_idManager)
{
for (int i = 0; i < table.Length; i++)
{
LinkedSlot linkedSlot = table[i].Value;
if (linkedSlot != null && linkedSlot.SlotArray != null)
{
linkedSlot.SlotArray = newTable;
newTable[i] = table[i];
}
}
}
table = newTable;
}
/// <summary>
/// Chooses the next larger table size
/// </summary>
private static int GetNewTableSize(int minSize)
{
if ((uint)minSize > MaxArrayLength)
{
// Intentionally return a value that will result in an OutOfMemoryException
return int.MaxValue;
}
//
// Round up the size to the next power of 2
//
// The algorithm takes three steps:
// input -> subtract one -> propagate 1-bits to the right -> add one
//
// Let's take a look at the 3 steps in both interesting cases: where the input
// is (Example 1) and isn't (Example 2) a power of 2.
//
// Example 1: 100000 -> 011111 -> 011111 -> 100000
// Example 2: 011010 -> 011001 -> 011111 -> 100000
//
int newSize = minSize;
// Step 1: Decrement
newSize--;
// Step 2: Propagate 1-bits to the right.
newSize |= newSize >> 1;
newSize |= newSize >> 2;
newSize |= newSize >> 4;
newSize |= newSize >> 8;
newSize |= newSize >> 16;
// Step 3: Increment
newSize++;
// Don't set newSize to more than Array.MaxArrayLength
if ((uint)newSize > MaxArrayLength)
{
newSize = MaxArrayLength;
}
return newSize;
}
/// <summary>
/// A wrapper struct used as LinkedSlotVolatile[] - an array of LinkedSlot instances, but with volatile semantics
/// on array accesses.
/// </summary>
private struct LinkedSlotVolatile
{
internal volatile LinkedSlot Value;
}
/// <summary>
/// A node in the doubly-linked list stored in the ThreadLocal instance.
///
/// The value is stored in one of two places:
///
/// 1. If SlotArray is not null, the value is in SlotArray.Table[id]
/// 2. If SlotArray is null, the value is in FinalValue.
/// </summary>
private sealed class LinkedSlot
{
internal LinkedSlot(LinkedSlotVolatile[] slotArray)
{
SlotArray = slotArray;
}
// The next LinkedSlot for this ThreadLocal<> instance
internal volatile LinkedSlot Next;
// The previous LinkedSlot for this ThreadLocal<> instance
internal volatile LinkedSlot Previous;
// The SlotArray that stores this LinkedSlot at SlotArray.Table[id].
internal volatile LinkedSlotVolatile[] SlotArray;
// The value for this slot.
internal T Value;
}
/// <summary>
/// A manager class that assigns IDs to ThreadLocal instances
/// </summary>
private class IdManager
{
// The next ID to try
private int m_nextIdToTry = 0;
// Stores whether each ID is free or not. Additionally, the object is also used as a lock for the IdManager.
private List<bool> m_freeIds = new List<bool>();
internal int GetId()
{
lock (m_freeIds)
{
int availableId = m_nextIdToTry;
while (availableId < m_freeIds.Count)
{
if (m_freeIds[availableId]) { break; }
availableId++;
}
if (availableId == m_freeIds.Count)
{
m_freeIds.Add(false);
}
else
{
m_freeIds[availableId] = false;
}
m_nextIdToTry = availableId + 1;
return availableId;
}
}
// Return an ID to the pool
internal void ReturnId(int id)
{
lock (m_freeIds)
{
m_freeIds[id] = true;
if (id < m_nextIdToTry) m_nextIdToTry = id;
}
}
}
/// <summary>
/// A class that facilitates ThreadLocal cleanup after a thread exits.
///
/// After a thread with an associated thread-local table has exited, the FinalizationHelper
/// is responsible for removing back-references to the table. Since an instance of FinalizationHelper
/// is only referenced from a single thread-local slot, the FinalizationHelper will be GC'd once
/// the thread has exited.
///
/// The FinalizationHelper then locates all LinkedSlot instances with back-references to the table
/// (all those LinkedSlot instances can be found by following references from the table slots) and
/// releases the table so that it can get GC'd.
/// </summary>
private class FinalizationHelper
{
internal LinkedSlotVolatile[] SlotArray;
private bool m_trackAllValues;
internal FinalizationHelper(LinkedSlotVolatile[] slotArray, bool trackAllValues)
{
SlotArray = slotArray;
m_trackAllValues = trackAllValues;
}
~FinalizationHelper()
{
LinkedSlotVolatile[] slotArray = SlotArray;
for (int i = 0; i < slotArray.Length; i++)
{
LinkedSlot linkedSlot = slotArray[i].Value;
if (linkedSlot == null)
{
// This slot in the table is empty
continue;
}
if (m_trackAllValues)
{
// Set the SlotArray field to null to release the slot array.
linkedSlot.SlotArray = null;
}
else
{
// Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to
// the table will be have been removed, and so the table can get GC'd.
lock (s_idManager)
{
if (linkedSlot.Next != null)
{
linkedSlot.Next.Previous = linkedSlot.Previous;
}
// Since the list uses a dummy head node, the Previous reference should never be null.
linkedSlot.Previous.Next = linkedSlot.Next;
}
}
}
}
}
}
/// <summary>A debugger view of the ThreadLocal&lt;T&gt; to surface additional debugging properties and
/// to ensure that the ThreadLocal&lt;T&gt; does not become initialized if it was not already.</summary>
internal sealed class SystemThreading_ThreadLocalDebugView<T>
{
//The ThreadLocal object being viewed.
private readonly ThreadLocal<T> m_tlocal;
/// <summary>Constructs a new debugger view object for the provided ThreadLocal object.</summary>
/// <param name="tlocal">A ThreadLocal object to browse in the debugger.</param>
public SystemThreading_ThreadLocalDebugView(ThreadLocal<T> tlocal)
{
m_tlocal = tlocal;
}
/// <summary>Returns whether the ThreadLocal object is initialized or not.</summary>
public bool IsValueCreated
{
get { return m_tlocal.IsValueCreated; }
}
/// <summary>Returns the value of the ThreadLocal object.</summary>
public T Value
{
get
{
return m_tlocal.ValueForDebugDisplay;
}
}
/// <summary>Return all values for all threads that have accessed this instance.</summary>
public List<T> Values
{
get
{
return m_tlocal.ValuesForDebugDisplay;
}
}
}
}
#endif

View File

@ -0,0 +1,80 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace Microsoft.Win32
{
/// <summary>
/// Impersonation of a user. Allows to execute code under another
/// user context.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
internal class WindowsImpersonatedIdentity : IDisposable, IIdentity
{
private const int LOGON_TYPE_NEW_CREDENTIALS = 9;
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
private const int LOGON32_PROVIDER_WINNT50 = 3;
#if NETSTANDARD || NETCOREAPP
#else
private WindowsImpersonationContext impersonationContext = null;
#endif
NativeMethods.SafeTokenHandle token;
private WindowsIdentity identity = null;
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public WindowsImpersonatedIdentity(string userName, string domainName, string password)
{
if (string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(domainName) && string.IsNullOrEmpty(password))
{
identity = WindowsIdentity.GetCurrent();
}
else
{
if (NativeMethods.LogonUser(userName, domainName, password, domainName == null ? LOGON_TYPE_NEW_CREDENTIALS : LOGON32_LOGON_INTERACTIVE, domainName == null ? LOGON32_PROVIDER_WINNT50 : LOGON32_PROVIDER_DEFAULT, out token) != 0)
{
#if NETSTANDARD || NETCOREAPP
if (!NativeMethods.ImpersonateLoggedOnUser(token.DangerousGetHandle()))
throw new Win32Exception();
#else
identity = new WindowsIdentity(token.DangerousGetHandle());
impersonationContext = identity.Impersonate();
#endif
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
public string AuthenticationType => identity?.AuthenticationType;
public bool IsAuthenticated => identity == null ? false : identity.IsAuthenticated;
public string Name => identity == null ? null : identity.Name;
public void Dispose()
{
#if NETSTANDARD || NETCOREAPP
NativeMethods.RevertToSelf();
#else
if (impersonationContext != null)
impersonationContext.Undo();
#endif
token?.Dispose();
if (identity != null)
identity.Dispose();
}
}
}

View File

@ -0,0 +1,128 @@
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;
using JetBrains.Annotations;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Abstract class for throwing a method specific exception.
/// </summary>
[DebuggerStepThrough, Serializable]
[PublicAPI]
public abstract class TSNotSupportedException : Exception
{
/// <summary>Defines the minimum supported version for the action not allowed by this exception.</summary>
protected readonly TaskCompatibility min;
private readonly string myMessage;
/// <summary>
/// Initializes a new instance of the <see cref="TSNotSupportedException"/> class.
/// </summary>
/// <param name="serializationInfo">The serialization information.</param>
/// <param name="streamingContext">The streaming context.</param>
protected TSNotSupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext)
: base(serializationInfo, streamingContext)
{
try { min = (TaskCompatibility)serializationInfo.GetValue("min", typeof(TaskCompatibility)); }
catch { min = TaskCompatibility.V1; }
}
internal TSNotSupportedException(TaskCompatibility minComp)
{
min = minComp;
var stackTrace = new StackTrace();
var stackFrame = stackTrace.GetFrame(2);
var methodBase = stackFrame.GetMethod();
myMessage = $"{methodBase.DeclaringType?.Name}.{methodBase.Name} is not supported on {LibName}";
}
internal TSNotSupportedException(string message, TaskCompatibility minComp)
{
myMessage = message;
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>
/// Gets the object data.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="context">The context.</param>
[SecurityCritical, SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new ArgumentNullException(nameof(info));
info.AddValue("min", min);
base.GetObjectData(info, context);
}
}
/// <summary>
/// Thrown when the calling method is not supported by Task Scheduler 1.0.
/// </summary>
[DebuggerStepThrough, Serializable]
public class NotV1SupportedException : TSNotSupportedException
{
/// <summary>
/// Initializes a new instance of the <see cref="NotV1SupportedException" /> class.
/// </summary>
/// <param name="serializationInfo">The serialization information.</param>
/// <param name="streamingContext">The streaming context.</param>
protected NotV1SupportedException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { }
internal NotV1SupportedException() : base(TaskCompatibility.V2) { }
/// <summary>
/// Initializes a new instance of the <see cref="NotV1SupportedException" /> class.
/// </summary>
/// <param name="message">The message.</param>
public NotV1SupportedException(string message) : base(message, TaskCompatibility.V2) { }
internal override string LibName => "Task Scheduler 1.0";
}
/// <summary>
/// Thrown when the calling method is not supported by Task Scheduler 2.0.
/// </summary>
[DebuggerStepThrough, Serializable]
public class NotV2SupportedException : TSNotSupportedException
{
/// <summary>
/// Initializes a new instance of the <see cref="NotV1SupportedException" /> class.
/// </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)";
}
/// <summary>
/// Thrown when the calling method is not supported by Task Scheduler versions prior to the one specified.
/// </summary>
[DebuggerStepThrough, Serializable]
public class NotSupportedPriorToException : TSNotSupportedException
{
/// <summary>
/// Initializes a new instance of the <see cref="NotV1SupportedException" /> class.
/// </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

@ -0,0 +1,702 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.Win32.TaskScheduler.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Win32.TaskScheduler.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Call a COM object.
/// </summary>
internal static string ActionTypeComHandler {
get {
return ResourceManager.GetString("ActionTypeComHandler", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Start a program.
/// </summary>
internal static string ActionTypeExecute {
get {
return ResourceManager.GetString("ActionTypeExecute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Send an e-mail.
/// </summary>
internal static string ActionTypeSendEmail {
get {
return ResourceManager.GetString("ActionTypeSendEmail", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Display a message.
/// </summary>
internal static string ActionTypeShowMessage {
get {
return ResourceManager.GetString("ActionTypeShowMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {3} {0:P}.
/// </summary>
internal static string ComHandlerAction {
get {
return ResourceManager.GetString("ComHandlerAction", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to every day.
/// </summary>
internal static string DOWAllDays {
get {
return ResourceManager.GetString("DOWAllDays", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {1} {0}.
/// </summary>
internal static string EmailAction {
get {
return ResourceManager.GetString("EmailAction", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ..
/// </summary>
internal static string EndSentence {
get {
return ResourceManager.GetString("EndSentence", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The date and time a trigger expires must be later than the time time it starts or is activated..
/// </summary>
internal static string Error_TriggerEndBeforeStart {
get {
return ResourceManager.GetString("Error_TriggerEndBeforeStart", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} {1}.
/// </summary>
internal static string ExecAction {
get {
return ResourceManager.GetString("ExecAction", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to -.
/// </summary>
internal static string HyphenSeparator {
get {
return ResourceManager.GetString("HyphenSeparator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ,.
/// </summary>
internal static string ListSeparator {
get {
return ResourceManager.GetString("ListSeparator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to every month.
/// </summary>
internal static string MOYAllMonths {
get {
return ResourceManager.GetString("MOYAllMonths", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Multiple actions defined.
/// </summary>
internal static string MultipleActions {
get {
return ResourceManager.GetString("MultipleActions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Multiple triggers defined.
/// </summary>
internal static string MultipleTriggers {
get {
return ResourceManager.GetString("MultipleTriggers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0}.
/// </summary>
internal static string ShowMessageAction {
get {
return ResourceManager.GetString("ShowMessageAction", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Author.
/// </summary>
internal static string TaskDefaultPrincipal {
get {
return ResourceManager.GetString("TaskDefaultPrincipal", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Disabled.
/// </summary>
internal static string TaskStateDisabled {
get {
return ResourceManager.GetString("TaskStateDisabled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Queued.
/// </summary>
internal static string TaskStateQueued {
get {
return ResourceManager.GetString("TaskStateQueued", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Ready.
/// </summary>
internal static string TaskStateReady {
get {
return ResourceManager.GetString("TaskStateReady", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Running.
/// </summary>
internal static string TaskStateRunning {
get {
return ResourceManager.GetString("TaskStateRunning", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unknown.
/// </summary>
internal static string TaskStateUnknown {
get {
return ResourceManager.GetString("TaskStateUnknown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to any user.
/// </summary>
internal static string TriggerAnyUser {
get {
return ResourceManager.GetString("TriggerAnyUser", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At system startup.
/// </summary>
internal static string TriggerBoot1 {
get {
return ResourceManager.GetString("TriggerBoot1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom Trigger.
/// </summary>
internal static string TriggerCustom1 {
get {
return ResourceManager.GetString("TriggerCustom1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} every day.
/// </summary>
internal static string TriggerDaily1 {
get {
return ResourceManager.GetString("TriggerDaily1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} every {1} days.
/// </summary>
internal static string TriggerDaily2 {
get {
return ResourceManager.GetString("TriggerDaily2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to indefinitely.
/// </summary>
internal static string TriggerDuration0 {
get {
return ResourceManager.GetString("TriggerDuration0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to for a duration of {0}.
/// </summary>
internal static string TriggerDurationNot0 {
get {
return ResourceManager.GetString("TriggerDurationNot0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to for {0}.
/// </summary>
internal static string TriggerDurationNot0Short {
get {
return ResourceManager.GetString("TriggerDurationNot0Short", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Trigger expires at {0:G}..
/// </summary>
internal static string TriggerEndBoundary {
get {
return ResourceManager.GetString("TriggerEndBoundary", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom event filter.
/// </summary>
internal static string TriggerEvent1 {
get {
return ResourceManager.GetString("TriggerEvent1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On event - Log: {0}.
/// </summary>
internal static string TriggerEventBasic1 {
get {
return ResourceManager.GetString("TriggerEventBasic1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to , Source: {0}.
/// </summary>
internal static string TriggerEventBasic2 {
get {
return ResourceManager.GetString("TriggerEventBasic2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to , EventID: {0}.
/// </summary>
internal static string TriggerEventBasic3 {
get {
return ResourceManager.GetString("TriggerEventBasic3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to When computer is idle.
/// </summary>
internal static string TriggerIdle1 {
get {
return ResourceManager.GetString("TriggerIdle1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At log on of {0}.
/// </summary>
internal static string TriggerLogon1 {
get {
return ResourceManager.GetString("TriggerLogon1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} on day {1} of {2}, starting {0:d}.
/// </summary>
internal static string TriggerMonthly1 {
get {
return ResourceManager.GetString("TriggerMonthly1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} on {1} {2:f} each {3}, starting {0:d}.
/// </summary>
internal static string TriggerMonthlyDOW1 {
get {
return ResourceManager.GetString("TriggerMonthlyDOW1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to When the task is created or modified.
/// </summary>
internal static string TriggerRegistration1 {
get {
return ResourceManager.GetString("TriggerRegistration1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to After triggered, repeat every {0} {1}..
/// </summary>
internal static string TriggerRepetition {
get {
return ResourceManager.GetString("TriggerRepetition", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Every {0} {1}..
/// </summary>
internal static string TriggerRepetitionShort {
get {
return ResourceManager.GetString("TriggerRepetitionShort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On local connection to {0}..
/// </summary>
internal static string TriggerSessionConsoleConnect {
get {
return ResourceManager.GetString("TriggerSessionConsoleConnect", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On local disconnect from {0}..
/// </summary>
internal static string TriggerSessionConsoleDisconnect {
get {
return ResourceManager.GetString("TriggerSessionConsoleDisconnect", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On remote connection to {0}..
/// </summary>
internal static string TriggerSessionRemoteConnect {
get {
return ResourceManager.GetString("TriggerSessionRemoteConnect", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On remote disconnect from {0}..
/// </summary>
internal static string TriggerSessionRemoteDisconnect {
get {
return ResourceManager.GetString("TriggerSessionRemoteDisconnect", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On workstation lock of {0}..
/// </summary>
internal static string TriggerSessionSessionLock {
get {
return ResourceManager.GetString("TriggerSessionSessionLock", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On workstation unlock of {0}..
/// </summary>
internal static string TriggerSessionSessionUnlock {
get {
return ResourceManager.GetString("TriggerSessionSessionUnlock", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to user session of {0}.
/// </summary>
internal static string TriggerSessionUserSession {
get {
return ResourceManager.GetString("TriggerSessionUserSession", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} on {0:d}.
/// </summary>
internal static string TriggerTime1 {
get {
return ResourceManager.GetString("TriggerTime1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At startup.
/// </summary>
internal static string TriggerTypeBoot {
get {
return ResourceManager.GetString("TriggerTypeBoot", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom Trigger.
/// </summary>
internal static string TriggerTypeCustom {
get {
return ResourceManager.GetString("TriggerTypeCustom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Daily.
/// </summary>
internal static string TriggerTypeDaily {
get {
return ResourceManager.GetString("TriggerTypeDaily", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On an event.
/// </summary>
internal static string TriggerTypeEvent {
get {
return ResourceManager.GetString("TriggerTypeEvent", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On idle.
/// </summary>
internal static string TriggerTypeIdle {
get {
return ResourceManager.GetString("TriggerTypeIdle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At log on.
/// </summary>
internal static string TriggerTypeLogon {
get {
return ResourceManager.GetString("TriggerTypeLogon", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Monthly.
/// </summary>
internal static string TriggerTypeMonthly {
get {
return ResourceManager.GetString("TriggerTypeMonthly", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Monthly.
/// </summary>
internal static string TriggerTypeMonthlyDOW {
get {
return ResourceManager.GetString("TriggerTypeMonthlyDOW", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At task creation/modification.
/// </summary>
internal static string TriggerTypeRegistration {
get {
return ResourceManager.GetString("TriggerTypeRegistration", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to On state change.
/// </summary>
internal static string TriggerTypeSessionStateChange {
get {
return ResourceManager.GetString("TriggerTypeSessionStateChange", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to One time.
/// </summary>
internal static string TriggerTypeTime {
get {
return ResourceManager.GetString("TriggerTypeTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Weekly.
/// </summary>
internal static string TriggerTypeWeekly {
get {
return ResourceManager.GetString("TriggerTypeWeekly", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} every {1} of every week, starting {0:d}.
/// </summary>
internal static string TriggerWeekly1Week {
get {
return ResourceManager.GetString("TriggerWeekly1Week", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to At {0:t} every {1} of every {2} weeks, starting {0:d}.
/// </summary>
internal static string TriggerWeeklyMultWeeks {
get {
return ResourceManager.GetString("TriggerWeeklyMultWeeks", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to every.
/// </summary>
internal static string WWAllWeeks {
get {
return ResourceManager.GetString("WWAllWeeks", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to fifth.
/// </summary>
internal static string WWFifthWeek {
get {
return ResourceManager.GetString("WWFifthWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to first.
/// </summary>
internal static string WWFirstWeek {
get {
return ResourceManager.GetString("WWFirstWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to fourth.
/// </summary>
internal static string WWFourthWeek {
get {
return ResourceManager.GetString("WWFourthWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to last.
/// </summary>
internal static string WWLastWeek {
get {
return ResourceManager.GetString("WWLastWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to second.
/// </summary>
internal static string WWSecondWeek {
get {
return ResourceManager.GetString("WWSecondWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to third.
/// </summary>
internal static string WWThirdWeek {
get {
return ResourceManager.GetString("WWThirdWeek", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,357 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ActionTypeComHandler" xml:space="preserve">
<value>Call a COM object</value>
</data>
<data name="ActionTypeExecute" xml:space="preserve">
<value>Start a program</value>
</data>
<data name="ActionTypeSendEmail" xml:space="preserve">
<value>Send an e-mail</value>
</data>
<data name="ActionTypeShowMessage" xml:space="preserve">
<value>Display a message</value>
</data>
<data name="ComHandlerAction" xml:space="preserve">
<value>{3} {0:P}</value>
<comment>0 = Class GUID; 1 = Data; 2 = Id; 3 = Name</comment>
</data>
<data name="DOWAllDays" xml:space="preserve">
<value>every day</value>
</data>
<data name="EmailAction" xml:space="preserve">
<value>{1} {0}</value>
<comment>0 = Subject; 1 = To; 2 = Cc, 3 = Bcc, 4 = From, 5 = ReplyTo, 6 = Body, 7 = Server, 8 = Id</comment>
</data>
<data name="EndSentence" xml:space="preserve">
<value>.</value>
</data>
<data name="ExecAction" xml:space="preserve">
<value>{0} {1}</value>
<comment>0 = Executable Path; 1 = Arguments; 2 = WorkingDirectory; 3 = Id</comment>
</data>
<data name="HyphenSeparator" xml:space="preserve">
<value>-</value>
</data>
<data name="ListSeparator" xml:space="preserve">
<value>,</value>
</data>
<data name="MOYAllMonths" xml:space="preserve">
<value>every month</value>
</data>
<data name="MultipleActions" xml:space="preserve">
<value>Multiple actions defined</value>
</data>
<data name="MultipleTriggers" xml:space="preserve">
<value>Multiple triggers defined</value>
</data>
<data name="ShowMessageAction" xml:space="preserve">
<value>{0}</value>
<comment>0 = Title; 1 = MessageBody; 2 = Id</comment>
</data>
<data name="TaskDefaultPrincipal" xml:space="preserve">
<value>Author</value>
</data>
<data name="TaskStateDisabled" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="TaskStateQueued" xml:space="preserve">
<value>Queued</value>
</data>
<data name="TaskStateReady" xml:space="preserve">
<value>Ready</value>
</data>
<data name="TaskStateRunning" xml:space="preserve">
<value>Running</value>
</data>
<data name="TaskStateUnknown" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="TriggerAnyUser" xml:space="preserve">
<value>any user</value>
</data>
<data name="TriggerBoot1" xml:space="preserve">
<value>At system startup</value>
</data>
<data name="TriggerCustom1" xml:space="preserve">
<value>Custom Trigger</value>
</data>
<data name="TriggerDaily1" xml:space="preserve">
<value>At {0:t} every day</value>
</data>
<data name="TriggerDaily2" xml:space="preserve">
<value>At {0:t} every {1} days</value>
</data>
<data name="TriggerDuration0" xml:space="preserve">
<value>indefinitely</value>
</data>
<data name="TriggerDurationNot0" xml:space="preserve">
<value>for a duration of {0}</value>
<comment>0 = Duration</comment>
</data>
<data name="TriggerDurationNot0Short" xml:space="preserve">
<value>for {0}</value>
<comment>0 = Duration</comment>
</data>
<data name="TriggerEndBoundary" xml:space="preserve">
<value>Trigger expires at {0:G}.</value>
<comment>0 = EndBoundary</comment>
</data>
<data name="TriggerEvent1" xml:space="preserve">
<value>Custom event filter</value>
</data>
<data name="TriggerEventBasic1" xml:space="preserve">
<value>On event - Log: {0}</value>
<comment>0 = Log name</comment>
</data>
<data name="TriggerEventBasic2" xml:space="preserve">
<value>, Source: {0}</value>
<comment>0 = Source name (appended after log)</comment>
</data>
<data name="TriggerEventBasic3" xml:space="preserve">
<value>, EventID: {0}</value>
<comment>0 = Event ID (appended after log or source)</comment>
</data>
<data name="TriggerIdle1" xml:space="preserve">
<value>When computer is idle</value>
</data>
<data name="TriggerLogon1" xml:space="preserve">
<value>At log on of {0}</value>
</data>
<data name="TriggerMonthly1" xml:space="preserve">
<value>At {0:t} on day {1} of {2}, starting {0:d}</value>
<comment>0 = StartBoundary; 1 = list of Days; 2 = list of Months</comment>
</data>
<data name="TriggerMonthlyDOW1" xml:space="preserve">
<value>At {0:t} on {1} {2:f} each {3}, starting {0:d}</value>
<comment>0 = StartBoundary; 1 = list of weeks of Month; 2 = list of Week Days; 3 = list of Months</comment>
</data>
<data name="TriggerRegistration1" xml:space="preserve">
<value>When the task is created or modified</value>
</data>
<data name="TriggerRepetition" xml:space="preserve">
<value>After triggered, repeat every {0} {1}.</value>
<comment>0 = Interval; 1= Duration string</comment>
</data>
<data name="TriggerRepetitionShort" xml:space="preserve">
<value>Every {0} {1}.</value>
<comment>0 = Interval; 1= Duration string</comment>
</data>
<data name="TriggerSessionConsoleConnect" xml:space="preserve">
<value>On local connection to {0}.</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerSessionConsoleDisconnect" xml:space="preserve">
<value>On local disconnect from {0}.</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerSessionRemoteConnect" xml:space="preserve">
<value>On remote connection to {0}.</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerSessionRemoteDisconnect" xml:space="preserve">
<value>On remote disconnect from {0}.</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerSessionSessionLock" xml:space="preserve">
<value>On workstation lock of {0}.</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerSessionSessionUnlock" xml:space="preserve">
<value>On workstation unlock of {0}.</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerSessionUserSession" xml:space="preserve">
<value>user session of {0}</value>
<comment>0 = UserId</comment>
</data>
<data name="TriggerTime1" xml:space="preserve">
<value>At {0:t} on {0:d}</value>
<comment>0 = StartBoundary</comment>
</data>
<data name="TriggerTypeBoot" xml:space="preserve">
<value>At startup</value>
</data>
<data name="TriggerTypeCustom" xml:space="preserve">
<value>Custom Trigger</value>
</data>
<data name="TriggerTypeDaily" xml:space="preserve">
<value>Daily</value>
</data>
<data name="TriggerTypeEvent" xml:space="preserve">
<value>On an event</value>
</data>
<data name="TriggerTypeIdle" xml:space="preserve">
<value>On idle</value>
</data>
<data name="TriggerTypeLogon" xml:space="preserve">
<value>At log on</value>
</data>
<data name="TriggerTypeMonthly" xml:space="preserve">
<value>Monthly</value>
</data>
<data name="TriggerTypeMonthlyDOW" xml:space="preserve">
<value>Monthly</value>
</data>
<data name="TriggerTypeRegistration" xml:space="preserve">
<value>At task creation/modification</value>
</data>
<data name="TriggerTypeSessionStateChange" xml:space="preserve">
<value>On state change</value>
</data>
<data name="TriggerTypeTime" xml:space="preserve">
<value>One time</value>
</data>
<data name="TriggerTypeWeekly" xml:space="preserve">
<value>Weekly</value>
</data>
<data name="TriggerWeekly1Week" xml:space="preserve">
<value>At {0:t} every {1} of every week, starting {0:d}</value>
<comment>0 = StartBoundary; 1 = list of Week Days</comment>
</data>
<data name="TriggerWeeklyMultWeeks" xml:space="preserve">
<value>At {0:t} every {1} of every {2} weeks, starting {0:d}</value>
<comment>0 = StartBoundary; 1 = list of Week Days; 2 = WeekInterval</comment>
</data>
<data name="WWAllWeeks" xml:space="preserve">
<value>every</value>
</data>
<data name="WWFifthWeek" xml:space="preserve">
<value>fifth</value>
</data>
<data name="WWFirstWeek" xml:space="preserve">
<value>first</value>
</data>
<data name="WWFourthWeek" xml:space="preserve">
<value>fourth</value>
</data>
<data name="WWLastWeek" xml:space="preserve">
<value>last</value>
</data>
<data name="WWSecondWeek" xml:space="preserve">
<value>second</value>
</data>
<data name="WWThirdWeek" xml:space="preserve">
<value>third</value>
</data>
<data name="Error_TriggerEndBeforeStart" xml:space="preserve">
<value>The date and time a trigger expires must be later than the time time it starts or is activated.</value>
</data>
</root>

View File

@ -0,0 +1,132 @@
namespace System.Reflection
{
/// <summary>Extensions related to <c>System.Reflection</c></summary>
internal static class ReflectionHelper
{
/// <summary>Loads a type from a named assembly.</summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="asmRef">The name or path of the file that contains the manifest of the assembly.</param>
/// <returns>The <see cref="Type"/> reference, or <c>null</c> if type or assembly not found.</returns>
public static Type LoadType(string typeName, string asmRef)
{
Type ret = null;
if (!TryGetType(Assembly.GetCallingAssembly(), typeName, ref ret))
if (!TryGetType(asmRef, typeName, ref ret))
if (!TryGetType(Assembly.GetExecutingAssembly(), typeName, ref ret))
TryGetType(Assembly.GetEntryAssembly(), typeName, ref ret);
return ret;
}
/// <summary>Tries the retrieve a <see cref="Type"/> reference from an assembly.</summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="asmRef">The assembly reference name from which to load the type.</param>
/// <param name="type">The <see cref="Type"/> reference, if found.</param>
/// <returns><c>true</c> if the type was found in the assembly; otherwise, <c>false</c>.</returns>
private static bool TryGetType(string asmRef, string typeName, ref Type type)
{
try
{
if (System.IO.File.Exists(asmRef))
return TryGetType(Assembly.LoadFrom(asmRef), typeName, ref type);
}
catch { }
return false;
}
/// <summary>Tries the retrieve a <see cref="Type"/> reference from an assembly.</summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="asm">The assembly from which to load the type.</param>
/// <param name="type">The <see cref="Type"/> reference, if found.</param>
/// <returns><c>true</c> if the type was found in the assembly; otherwise, <c>false</c>.</returns>
private static bool TryGetType(Assembly asm, string typeName, ref Type type)
{
if (asm != null)
{
try
{
type = asm.GetType(typeName, false, false);
return (type != null);
}
catch { }
}
return false;
}
/// <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>
/// <param name="instArgs">The arguments to supply to the 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, object[] instArgs, string methodName, params object[] args)
{
object o = Activator.CreateInstance(type, instArgs);
return InvokeMethod<T>(o, methodName, args);
}
/// <summary>Invokes a named method on an object with parameters and no return value.</summary>
/// <param name="obj">The object on which to invoke the method.</param>
/// <param name="methodName">Name of the method.</param>
/// <param name="args">The arguments to provide to the method invocation.</param>
public static T InvokeMethod<T>(object obj, string methodName, params object[] args)
{
Type[] argTypes = (args == null || args.Length == 0) ? Type.EmptyTypes : Array.ConvertAll(args, delegate(object o) { return o == null ? typeof(object) : o.GetType(); });
return InvokeMethod<T>(obj, methodName, argTypes, args);
}
/// <summary>Invokes a named method on an object with parameters and no return value.</summary>
/// <typeparam name="T">The expected type of the method's return value.</typeparam>
/// <param name="obj">The object on which to invoke the method.</param>
/// <param name="methodName">Name of the method.</param>
/// <param name="argTypes">The types of the <paramref name="args"/>.</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>(object obj, string methodName, Type[] argTypes, object[] args)
{
MethodInfo mi = obj?.GetType().GetMethod(methodName, argTypes);
if (mi != null)
return (T)Convert.ChangeType(mi.Invoke(obj, args), typeof(T));
return default(T);
}
/// <summary>Gets a named property value from an object.</summary>
/// <typeparam name="T">The expected type of the property to be returned.</typeparam>
/// <param name="obj">The object from which to retrieve the property.</param>
/// <param name="propName">Name of the property.</param>
/// <param name="defaultValue">The default value to return in the instance that the property is not found.</param>
/// <returns>The property value, if found, or the <paramref name="defaultValue"/> if not.</returns>
public static T GetProperty<T>(object obj, string propName, T defaultValue = default(T))
{
if (obj != null)
{
try { return (T)Convert.ChangeType(obj.GetType().InvokeMember(propName, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, obj, null, null), typeof(T)); }
catch { }
}
return defaultValue;
}
/// <summary>Sets a named property on an object.</summary>
/// <typeparam name="T">The type of the property to be set.</typeparam>
/// <param name="obj">The object on which to set the property.</param>
/// <param name="propName">Name of the property.</param>
/// <param name="value">The property value to set on the object.</param>
public static void SetProperty<T>(object obj, string propName, T value)
{
try { obj?.GetType().InvokeMember(propName, BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, obj, new object[] { value }, null); }
catch { }
}
}
}

View File

@ -0,0 +1,124 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using JetBrains.Annotations;
namespace Microsoft.Win32.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="Microsoft.Win32.TaskScheduler.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>
/// Parses the input string. String must be in the format "$(@ [Dll], [ResourceID])".
/// </summary>
/// <param name="value">The input string value.</param>
/// <returns>A new <see cref="ResourceReferenceValue" /> instance on success or <c>null</c> on failure.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <c>null</c></exception>
/// <exception cref="FormatException"><paramref name="value"/> is not in the format "$(@ [Dll], [ResourceID])"</exception>
[NotNull]
public static ResourceReferenceValue Parse([NotNull] string value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
ResourceReferenceValue r;
if (!TryParse(value, out r))
throw new FormatException();
return r;
}
/// <summary>
/// Tries to parse to input string. String must be in the format "$(@ [Dll], [ResourceID])".
/// </summary>
/// <param name="value">The input string value.</param>
/// <param name="resourceRef">The resource reference to be returned. On failure, this value equals <c>null</c>.</param>
/// <returns>A new <see cref="ResourceReferenceValue"/> instance on success or <c>null</c> on failure.</returns>
public static bool TryParse(string value, out ResourceReferenceValue resourceRef)
{
if (!string.IsNullOrEmpty(value))
{
var m = System.Text.RegularExpressions.Regex.Match(value, @"^\$\(\@ (?<x>[^,]+), (?<i>-?\d+)\)$");
if (m.Success)
{
resourceRef = new ResourceReferenceValue(m.Groups["x"].Value, int.Parse(m.Groups["i"].Value));
return true;
}
}
resourceRef = null;
return false;
}
/// <summary>
/// Gets the result of pulling the string from the resource file using the identifier.
/// </summary>
/// <returns><see cref="string"/> from resource file.</returns>
/// <exception cref="System.IO.FileNotFoundException"><see cref="ResourceFilePath"/> cannot be found.</exception>
/// <exception cref="System.ComponentModel.Win32Exception">Unable to load <see cref="ResourceFilePath"/> or string identified by <see cref="ResourceIdentifier"/>.</exception>
[NotNull]
public string GetResolvedString()
{
if (!System.IO.File.Exists(ResourceFilePath))
throw new System.IO.FileNotFoundException("Invalid resource file path.", ResourceFilePath);
IntPtr hLib = IntPtr.Zero;
try
{
hLib = NativeMethods.LoadLibrary(ResourceFilePath);
if (hLib == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
var sb = new StringBuilder(8192);
int l = LoadString(hLib, ResourceIdentifier, sb, sb.Capacity);
if (l == 0)
throw new System.ComponentModel.Win32Exception();
return sb.ToString(0, l);
}
finally
{
if (hLib != IntPtr.Zero)
NativeMethods.FreeLibrary(hLib);
}
}
/// <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})";
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern int LoadString(IntPtr hInstance, int wID, StringBuilder lpBuffer, int nBufferMax);
}
}

View File

@ -0,0 +1,409 @@
#if !(NET20 || NET35 || NET40)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>Abstract class representing a secured item for storage in a <see cref="TaskSchedulerSnapshot"/>.</summary>
public abstract class SnapshotItem
{
/// <summary>Initializes a new instance of the <see cref="SnapshotItem"/> class.</summary>
/// <param name="path">The path to the item.</param>
/// <param name="sddl">The SDDL for the item.</param>
internal SnapshotItem(string path, string sddl) { Path = path; Sddl = sddl; }
/// <summary>Gets the path to the item.</summary>
/// <value>The path to the item.</value>
public string Path { get; private set; }
/// <summary>Gets the SDDL for the item.</summary>
/// <value>The SDDL for the item.</value>
public string Sddl { get; private set; }
}
/// <summary>Represents a <see cref="TaskFolder"/> instance and captures its name and security.</summary>
public sealed class TaskFolderSnapshot : SnapshotItem
{
/// <summary>Initializes a new instance of the <see cref="TaskFolderSnapshot"/> class.</summary>
/// <param name="path">The path to the item.</param>
/// <param name="sddl">The SDDL for the item.</param>
internal TaskFolderSnapshot(string path, string sddl) : base(path, sddl) { }
}
/// <summary>
/// Represents all the information about the tasks and folders from a <see cref="TaskService"/> instance that can be used to reconstitute tasks and folders
/// on the same or different systems. <note>This class and related classes are only available under the .NET 4.5.2 build and later .NET versions due to
/// dependencies on threading and compressed (zip) files.</note>
/// </summary>
public sealed class TaskSchedulerSnapshot : IXmlSerializable
{
private const string hdrfile = ".metadata.xml";
private List<SnapshotItem> items;
/// <summary>Creates a new instance of <see cref="TaskSchedulerSnapshot"/> from an existing snapshot.</summary>
/// <param name="path">The zip file snapshot created by the <see cref="Create(TaskService,string)"/> method.</param>
public TaskSchedulerSnapshot(string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (!File.Exists(path)) throw new FileNotFoundException("Invalid file location.", nameof(path));
try
{
using (var zip = ZipFile.OpenRead(path))
zip.GetEntry(hdrfile);
}
catch
{
throw new InvalidOperationException("Invalid file format.");
}
Path = path;
}
internal TaskSchedulerSnapshot() { }
/// <summary>
/// Gets a list of <see cref="TaskSnapshot"/> and <see cref="TaskFolderSnapshot"/> instances the represent the tasks and folders from a Task Scheduler instance.
/// </summary>
public List<SnapshotItem> Items
{
get => items ?? (items = (Path == null ? new List<SnapshotItem>() : GetArchiveItems(Path)));
internal set => items = value;
}
/// <summary>Gets the path of the file based snapshot.</summary>
public string Path { get; private set; }
/// <summary>Gets the machine name of the server from which the snapshot was taken.</summary>
/// <value>The target server name.</value>
public string TargetServer { get; private set; }
/// <summary>Gets the UTC time stamp for when the snapshot was taken.</summary>
/// <value>The time stamp.</value>
public DateTime TimeStamp { get; private set; } = DateTime.UtcNow;
/// <summary>
/// Creates a compressed zip file that contains all the information accessible to the user from the <see cref="TaskService"/> instance necessary to
/// reconstitute its tasks and folders. <note>This method can take many seconds to execute. It is recommended to call the asynchronous
/// version.</note><note type="warning">This method will execute without error even if the user does not have permissions to see all tasks and folders.
/// It is imperative that the developer ensures that the user has Administrator or equivalent rights before calling this method.</note>
/// </summary>
/// <param name="ts">The <see cref="TaskService"/> from which to pull the tasks and folders.</param>
/// <param name="path">The output zip file in which to place the snapshot information.</param>
/// <returns>A <see cref="TaskSchedulerSnapshot"/> instance with the contents of the specified Task Scheduler connection.</returns>
public static TaskSchedulerSnapshot Create(TaskService ts, string path)
{
var c = new System.Threading.CancellationTokenSource();
return InternalCreate(ts.Token, path, c.Token, null);
}
/// <summary>
/// Creates a compressed zip file that contains all the information accessible to the user from the <see cref="TaskService"/> instance necessary to
/// reconstitute its tasks and folders. <note type="warning">This method will execute without error even if the user does not have permissions to see all
/// tasks and folders. It is imperative that the developer ensures that the user has Administrator or equivalent rights before calling this method.</note>
/// </summary>
/// <param name="tsToken">The <see cref="TaskService.ConnectionToken"/> from which to pull the tasks and folders.</param>
/// <param name="path">The output zip file in which to place the snapshot information.</param>
/// <param name="cancelToken">A cancellation token to use to cancel this asynchronous operation.</param>
/// <param name="progress">An optional <see cref="IProgress{T}"/> instance to use to report progress of the asynchronous operation.</param>
/// <returns>An asynchronous <see cref="TaskSchedulerSnapshot"/> instance with the contents of the specified Task Scheduler connection.</returns>
public static async System.Threading.Tasks.Task<TaskSchedulerSnapshot> Create(TaskService.ConnectionToken tsToken, string path, System.Threading.CancellationToken cancelToken, IProgress<Tuple<int, string>> progress)
{
return await System.Threading.Tasks.Task.Run(() => InternalCreate(tsToken, path, cancelToken, progress), cancelToken);
}
/// <summary>Opens an existing snapshot and returns a new instance of <see cref="TaskSchedulerSnapshot"/>.</summary>
/// <param name="path">The zip file snapshot created by the <see cref="Create(TaskService,string)"/> method.</param>
/// <returns>A <see cref="TaskSchedulerSnapshot"/> instance with the contents of the specified snapshot file.</returns>
public static TaskSchedulerSnapshot Open(string path) => new TaskSchedulerSnapshot(path);
/// <summary>Register a list of snapshot items (tasks and folders) into the specified Task Scheduler.</summary>
/// <param name="tsToken">The <see cref="TaskService.ConnectionToken"/> into which the tasks and folders are registered.</param>
/// <param name="itemPaths">
/// The list of paths representing the tasks and folders from this snapshot that should be registered on the <see cref="TaskService"/> instance.
/// </param>
/// <param name="applyAccessRights">
/// If <c>true</c>, takes the access rights from the snapshot item and applies it to both new and existing tasks and folders.
/// </param>
/// <param name="overwriteExisting">
/// If <c>true</c>, overwrite any existing tasks and folders found in the target Task Scheduler that match the path of the snapshot item.
/// </param>
/// <param name="passwords">
/// Lookup table for password. Provide pairs of the user/group account name and the associated passwords for any task that requires a password.
/// </param>
/// <param name="cancelToken">A cancellation token to use to cancel this asynchronous operation.</param>
/// <param name="progress">An optional <see cref="IProgress{T}"/> instance to use to report progress of the asynchronous operation.</param>
/// <returns>An asynchronous <see cref="Task"/> instance.</returns>
public async System.Threading.Tasks.Task Restore(TaskService.ConnectionToken tsToken, IEnumerable<string> itemPaths, bool applyAccessRights, bool overwriteExisting, IDictionary<string, string> passwords, System.Threading.CancellationToken cancelToken, IProgress<Tuple<int, string>> progress)
{
if (itemPaths == null) throw new ArgumentNullException(nameof(itemPaths));
var items = Items.Where(i => i is TaskSnapshot).Join(itemPaths, a => a.Path, b => b, (a, b) => a).ToList();
if (items.Count != itemPaths.Count()) throw new ArgumentException($"Unable to locate matching tasks to all values of {nameof(itemPaths)}.", nameof(itemPaths));
await System.Threading.Tasks.Task.Run(() => InternalRestore(tsToken, items, applyAccessRights, overwriteExisting, passwords, cancelToken, progress));
}
/// <summary>Register a list of snapshot items (tasks and folders) into the specified Task Scheduler.</summary>
/// <param name="tsToken">The <see cref="TaskService.ConnectionToken"/> into which the tasks and folders are registered.</param>
/// <param name="items">
/// The list of <see cref="SnapshotItem"/> instances representing the tasks and folders from this snapshot that should be registered on the
/// <see cref="TaskService"/> instance.
/// </param>
/// <param name="applyAccessRights">
/// If <c>true</c>, takes the access rights from the snapshot item and applies it to both new and existing tasks and folders.
/// </param>
/// <param name="overwriteExisting">
/// If <c>true</c>, overwrite any existing tasks and folders found in the target Task Scheduler that match the path of the snapshot item.
/// </param>
/// <param name="passwords">
/// Lookup table for password. Provide pairs of the user/group account name and the associated passwords for any task that requires a password.
/// </param>
/// <param name="cancelToken">A cancellation token to use to cancel this asynchronous operation.</param>
/// <param name="progress">An optional <see cref="IProgress{T}"/> instance to use to report progress of the asynchronous operation.</param>
/// <returns>An asynchronous <see cref="Task"/> instance.</returns>
public async System.Threading.Tasks.Task Restore(TaskService.ConnectionToken tsToken, ICollection<SnapshotItem> items, bool applyAccessRights, bool overwriteExisting, IDictionary<string, string> passwords, System.Threading.CancellationToken cancelToken, IProgress<Tuple<int, string>> progress)
{
if (items == null) throw new ArgumentNullException(nameof(items));
await System.Threading.Tasks.Task.Run(() => InternalRestore(tsToken, items, applyAccessRights, overwriteExisting, passwords, cancelToken, progress));
}
/// <summary>Register a list of snapshot items (tasks and folders) into the specified Task Scheduler.</summary>
/// <param name="ts">The <see cref="TaskService"/> into which the tasks and folders are registered.</param>
/// <param name="items">
/// The list of <see cref="SnapshotItem"/> instances representing the tasks and folders from this snapshot that should be registered on the
/// <see cref="TaskService"/> instance.
/// </param>
/// <param name="applyAccessRights">
/// If <c>true</c>, takes the access rights from the snapshot item and applies it to both new and existing tasks and folders.
/// </param>
/// <param name="overwriteExisting">
/// If <c>true</c>, overwrite any existing tasks and folders found in the target Task Scheduler that match the path of the snapshot item.
/// </param>
/// <param name="passwords">
/// Lookup table for password. Provide pairs of the user/group account name and the associated passwords for any task that requires a password.
/// </param>
public void Restore(TaskService ts, ICollection<SnapshotItem> items, bool applyAccessRights, bool overwriteExisting, IDictionary<string, string> passwords)
{
var c = new System.Threading.CancellationTokenSource();
InternalRestore(ts.Token, items, applyAccessRights, overwriteExisting, passwords, c.Token, null);
}
XmlSchema IXmlSerializable.GetSchema() => null;
void IXmlSerializable.ReadXml(XmlReader reader)
{
reader.MoveToContent();
if (DateTime.TryParseExact(reader.GetAttribute("timestamp"), "o", CultureInfo.CurrentCulture.DateTimeFormat, DateTimeStyles.AssumeUniversal, out DateTime dt))
TimeStamp = dt;
TargetServer = reader.GetAttribute("machine");
reader.GetAttribute("version");
while (reader.Read())
{
if (reader.Name == "Task")
{
var tsnap = new TaskSnapshot(reader.GetAttribute("path"), reader.GetAttribute("sddl"), true);
var e = reader.GetAttribute("enabled");
if (e != null && e == "false") tsnap.Enabled = false;
Items.Add(tsnap);
}
else if (reader.Name == "TaskFolder")
Items.Add(new TaskFolderSnapshot(reader.GetAttribute("path"), reader.GetAttribute("sddl")));
}
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("timestamp", TimeStamp.ToString("o"));
if (TargetServer != null)
writer.WriteAttributeString("machine", TargetServer);
writer.WriteAttributeString("version", "1.0");
foreach (var i in items)
{
if (i is TaskSnapshot t)
{
writer.WriteStartElement("Task");
writer.WriteAttributeString("path", t.Path);
writer.WriteAttributeString("sddl", t.Sddl);
if (!t.Enabled)
writer.WriteAttributeString("enabled", "false");
writer.WriteEndElement();
}
else if (i is TaskFolderSnapshot f)
{
writer.WriteStartElement("TaskFolder");
writer.WriteAttributeString("path", f.Path);
writer.WriteAttributeString("sddl", f.Sddl);
writer.WriteEndElement();
}
}
}
private static List<SnapshotItem> GetArchiveItems(string archiveFile)
{
TaskSchedulerSnapshot ret = null;
using (var zip = ZipFile.OpenRead(archiveFile))
{
using (var hdrStr = zip.GetEntry(hdrfile).Open())
ret = new XmlSerializer(typeof(TaskSchedulerSnapshot)).Deserialize(hdrStr) as TaskSchedulerSnapshot;
if (ret == null) return null;
for (int i = 0; i < ret.Items.Count; i++)
{
if (ret.Items[i] is TaskSnapshot t)
{
var xml = zip.GetEntry(t.Path.TrimStart('\\') + ".xml");
using (var str = new StreamReader(xml.Open(), Encoding.UTF8))
t.TaskDefinitionXml = str.ReadToEnd();
}
}
}
return ret?.Items;
}
private static TaskSchedulerSnapshot InternalCreate(TaskService.ConnectionToken token, string path, System.Threading.CancellationToken cancelToken, IProgress<Tuple<int, string>> progress)
{
var ts = TaskService.CreateFromToken(token);
const SecurityInfos siall = SecurityInfos.DiscretionaryAcl | SecurityInfos.SystemAcl | SecurityInfos.Group | SecurityInfos.Owner;
if (File.Exists(path)) throw new ArgumentException("Output file already exists.", nameof(path));
int i = 0, count = 0;
using (var zipstr = new FileStream(path, FileMode.CreateNew))
using (var zip = new ZipArchive(zipstr, ZipArchiveMode.Create))
{
GetCount(ts.RootFolder);
var snapshot = new TaskSchedulerSnapshot { TargetServer = ts.TargetServer ?? Environment.MachineName };
GetContents(ts.RootFolder, snapshot.Items, zip);
snapshot.Items.Sort((t1, t2) => String.Compare(t1.Path, t2.Path, StringComparison.InvariantCultureIgnoreCase));
using (var hdr = new StreamWriter(zip.CreateEntry(hdrfile).Open(), Encoding.UTF8))
new XmlSerializer(snapshot.GetType()).Serialize(hdr, snapshot);
snapshot.Path = path;
return snapshot;
}
void GetCount(TaskFolder f)
{
count += f.Tasks.Count;
foreach (var sf in f.SubFolders)
{
count++;
GetCount(sf);
}
}
void GetContents(TaskFolder f, List<SnapshotItem> list, ZipArchive zip)
{
cancelToken.ThrowIfCancellationRequested();
foreach (var t in f.Tasks)
{
list.Add(new TaskSnapshot(t.Path, t.GetSecurityDescriptorSddlForm(siall), t.Enabled, t.Xml));
using (var wr = new StreamWriter(zip.CreateEntry(t.Path.TrimStart('\\') + ".xml").Open(), Encoding.Unicode))
wr.Write(t.Xml);
cancelToken.ThrowIfCancellationRequested();
progress?.Report(new Tuple<int, string>(++i * 100 / count, t.Path));
}
foreach (var sf in f.SubFolders)
{
list.Add(new TaskFolderSnapshot(sf.Path, sf.GetSecurityDescriptorSddlForm(siall)));
zip.CreateEntry(sf.Path.TrimStart('\\') + "\\");
GetContents(sf, list, zip);
cancelToken.ThrowIfCancellationRequested();
progress?.Report(new Tuple<int, string>(++i * 100 / count, sf.Path));
}
}
}
private void InternalRestore(TaskService.ConnectionToken token, ICollection<SnapshotItem> items, bool applyAccessRights, bool overwriteExisting, IDictionary<string, string> passwords, System.Threading.CancellationToken cancelToken, IProgress<Tuple<int, string>> progress)
{
var ts = TaskService.CreateFromToken(token);
var i = 0;
progress?.Report(new Tuple<int, string>(0, ""));
foreach (var item in items)
{
cancelToken.ThrowIfCancellationRequested();
if (item is TaskSnapshot t)
{
var td = ts.NewTask();
td.XmlText = t.TaskDefinitionXml;
var pwd = td.Principal.RequiresPassword() ? passwords?[td.Principal.ToString()] : null;
var st = ts.GetTask(t.Path);
if (st == null)
{
CreateTask(t, td, pwd);
}
else if (overwriteExisting)
{
st = st.Folder.RegisterTaskDefinition(System.IO.Path.GetFileName(t.Path), td, TaskCreation.CreateOrUpdate, td.Principal.ToString(), pwd, td.Principal.LogonType, applyAccessRights ? t.Sddl : null);
if (!t.Enabled) st.Enabled = false;
}
}
else if (item is TaskFolderSnapshot f)
{
var sf = ts.GetFolder(f.Path);
if (sf == null)
sf = EnsureFolder(f.Path);
else if (overwriteExisting && applyAccessRights)
sf.SetSecurityDescriptorSddlForm(f.Sddl);
}
progress?.Report(new Tuple<int, string>(++i * 100 / items.Count, item.Path));
}
progress?.Report(new Tuple<int, string>(100, ""));
void CreateTask(TaskSnapshot task, TaskDefinition td, string password)
{
var fpath = System.IO.Path.GetDirectoryName(task.Path);
var fld = EnsureFolder(fpath);
var t = fld.RegisterTaskDefinition(System.IO.Path.GetFileName(task.Path), td, TaskCreation.CreateOrUpdate, td.Principal.ToString(), password, td.Principal.LogonType, applyAccessRights ? task.Sddl : null);
if (!task.Enabled) t.Enabled = false;
}
TaskFolder EnsureFolder(string fpath)
{
if (!overwriteExisting)
{
var f = ts.GetFolder(fpath);
if (f != null) return f;
}
var flds = fpath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
var fld = ts.RootFolder;
var sfpath = "";
for (var j = 0; j < flds.Length; j++)
{
sfpath += "\\" + flds[j];
var sf = fld.SubFolders.Exists(flds[j]) ? fld.SubFolders[flds[j]] : null;
if (sf == null)
sf = fld.CreateFolder(flds[j], GetFolderSddl(sfpath), false);
else if (overwriteExisting)
sf.SetSecurityDescriptorSddlForm(GetFolderSddl(sfpath));
fld = sf;
}
return fld;
}
string GetFolderSddl(string path) => Items?.Find(tci => String.Equals(tci.Path, path, StringComparison.OrdinalIgnoreCase)).Sddl;
}
}
/// <summary>Represents a <see cref="Task"/> instance and captures its details.</summary>
public sealed class TaskSnapshot : SnapshotItem
{
/// <summary>Initializes a new instance of the <see cref="TaskSnapshot"/> class.</summary>
/// <param name="path">The path to the item.</param>
/// <param name="sddl">The SDDL for the item.</param>
/// <param name="enabled">If set to <c>true</c> task is enabled.</param>
/// <param name="xml">The XML for the <see cref="TaskDefinition"/>.</param>
internal TaskSnapshot(string path, string sddl, bool enabled, string xml = null) : base(path, sddl) { Enabled = enabled; TaskDefinitionXml = xml; }
/// <summary>Gets a value indicating whether th <see cref="Task"/> is enabled.</summary>
/// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
public bool Enabled { get; internal set; } = true;
/// <summary>Gets the <see cref="TaskDefinition"/> XML.</summary>
/// <value>The <see cref="TaskDefinition"/> XML.</value>
public string TaskDefinitionXml { get; internal set; }
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,400 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Collection of running tasks in a <see cref="TaskService"/>. This class has no public constructor and can only be accessed via the
/// properties and functions within <see cref="TaskService"/>.
/// </summary>
public sealed class RunningTaskCollection : IReadOnlyList<RunningTask>, IDisposable
{
private readonly TaskService svc;
private readonly V2Interop.IRunningTaskCollection v2Coll;
internal RunningTaskCollection([NotNull] TaskService svc) => this.svc = svc;
internal RunningTaskCollection([NotNull] TaskService svc, [NotNull] V2Interop.IRunningTaskCollection iTaskColl)
{
this.svc = svc;
v2Coll = iTaskColl;
}
/// <summary>Gets the number of registered tasks in the collection.</summary>
public int Count
{
get
{
if (v2Coll != null)
return v2Coll.Count;
var i = 0;
var v1Enum = new V1RunningTaskEnumerator(svc);
while (v1Enum.MoveNext())
i++;
return i;
}
}
/// <summary>Gets the specified running task from the collection.</summary>
/// <param name="index">The index of the running task to be retrieved.</param>
/// <returns>A <see cref="RunningTask"/> instance.</returns>
public RunningTask this[int index]
{
get
{
if (v2Coll != null)
{
var irt = v2Coll[++index];
return new RunningTask(svc, TaskService.GetTask(svc.v2TaskService, irt.Path), irt);
}
var i = 0;
var v1Enum = new V1RunningTaskEnumerator(svc);
while (v1Enum.MoveNext())
if (i++ == index)
return v1Enum.Current;
throw new ArgumentOutOfRangeException(nameof(index));
}
}
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
if (v2Coll != null)
Marshal.ReleaseComObject(v2Coll);
}
/// <summary>Gets an IEnumerator instance for this collection.</summary>
/// <returns>An enumerator.</returns>
public IEnumerator<RunningTask> GetEnumerator()
{
if (v2Coll != null)
return new ComEnumerator<RunningTask, V2Interop.IRunningTask>(() => v2Coll.Count, (object o) => v2Coll[o], o =>
{
V2Interop.IRegisteredTask task = null;
try { task = TaskService.GetTask(svc.v2TaskService, o.Path); } catch { }
return task == null ? null : new RunningTask(svc, task, o);
});
return new V1RunningTaskEnumerator(svc);
}
/// <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() => $"RunningTaskCollection; Count: {Count}";
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
private class V1RunningTaskEnumerator : IEnumerator<RunningTask>
{
private readonly TaskService svc;
private readonly TaskCollection.V1TaskEnumerator tEnum;
internal V1RunningTaskEnumerator([NotNull] TaskService svc)
{
this.svc = svc;
tEnum = new TaskCollection.V1TaskEnumerator(svc);
}
public RunningTask Current => new RunningTask(svc, tEnum.ICurrent);
object System.Collections.IEnumerator.Current => Current;
/// <summary>Releases all resources used by this class.</summary>
public void Dispose() => tEnum.Dispose();
public bool MoveNext() => tEnum.MoveNext() && (tEnum.Current?.State == TaskState.Running || MoveNext());
public void Reset() => tEnum.Reset();
}
}
/// <summary>
/// Contains all the tasks that are registered within a <see cref="TaskFolder"/>. This class has no public constructor and can only be
/// accessed via the properties and functions within <see cref="TaskFolder"/>.
/// </summary>
/// <remarks>
/// Potentially breaking change in 1.6.2 and later where under V1 the list previously included the '.job' extension on the task name.
/// This has been removed so that it is consistent with V2.
/// </remarks>
/// <example>
/// <code>public class Program
/// {
/// bool RootFolderHasTask(string taskName)
/// {
/// if (TaskService.Instance.RootFolder.Tasks.Count &gt; 0)
/// {
/// return TaskService.Instance.RootFolder.Tasks.Exists(taskName);
/// }
/// return false;
/// }
///
/// TaskCollection GetRootTasksStartingWith(string value)
/// {
/// var pattern = $"^{Regex.Escape(value)}.*$";
/// return TaskService.Instance.RootFolder.GetTasks(new Regex(pattern));
/// }
///
/// public static void Main()
/// {
/// foreach (var task in GetRootTasksStartingWith("MyCo"))
/// if (RootFolderHasTask(task.Name))
/// Console.WriteLine(task.Name);
/// }
/// }</code>
/// </example>
[PublicAPI]
public sealed class TaskCollection : IReadOnlyList<Task>, IDisposable
{
private readonly TaskFolder fld;
private readonly TaskService svc;
private readonly V2Interop.IRegisteredTaskCollection v2Coll;
private Regex filter;
private V1Interop.ITaskScheduler v1TS;
internal TaskCollection([NotNull] TaskService svc, Regex filter = null)
{
this.svc = svc;
Filter = filter;
v1TS = svc.v1TaskScheduler;
}
internal TaskCollection([NotNull] TaskFolder folder, [NotNull] V2Interop.IRegisteredTaskCollection iTaskColl, Regex filter = null)
{
svc = folder.TaskService;
Filter = filter;
fld = folder;
v2Coll = iTaskColl;
}
/// <summary>Gets the number of registered tasks in the collection.</summary>
public int Count
{
get
{
var i = 0;
if (v2Coll != null)
{
var v2Enum = new V2TaskEnumerator(fld, v2Coll, filter);
while (v2Enum.MoveNext())
i++;
}
else
{
var v1Enum = new V1TaskEnumerator(svc, filter);
return v1Enum.Count;
}
return i;
}
}
/// <summary>Gets or sets the regular expression filter for task names.</summary>
/// <value>The regular expression filter.</value>
private Regex Filter
{
get => filter;
set
{
var sfilter = value?.ToString().TrimStart('^').TrimEnd('$') ?? string.Empty;
if (sfilter == string.Empty || sfilter == "*")
filter = null;
else
{
if (value != null && value.ToString().TrimEnd('$').EndsWith("\\.job", StringComparison.InvariantCultureIgnoreCase))
filter = new Regex(value.ToString().Replace("\\.job", ""));
else
filter = value;
}
}
}
/// <summary>Gets the specified registered task from the collection.</summary>
/// <param name="index">The index of the registered task to be retrieved.</param>
/// <returns>A <see cref="Task"/> instance that contains the requested context.</returns>
public Task this[int index]
{
get
{
var i = 0;
var te = GetEnumerator();
while (te.MoveNext())
if (i++ == index)
return te.Current;
throw new ArgumentOutOfRangeException(nameof(index));
}
}
/// <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()
{
v1TS = null;
if (v2Coll != null)
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()
{
if (v1TS != null)
return new V1TaskEnumerator(svc, filter);
return new V2TaskEnumerator(fld, v2Coll, filter);
}
/// <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() => $"TaskCollection; Count: {Count}";
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
internal class V1TaskEnumerator : IEnumerator<Task>
{
private readonly Regex filter;
private readonly TaskService svc;
private readonly V1Interop.IEnumWorkItems wienum;
private string curItem;
private V1Interop.ITaskScheduler ts;
/// <summary>Internal constructor</summary>
/// <param name="svc">TaskService instance</param>
/// <param name="filter">The filter.</param>
internal V1TaskEnumerator(TaskService svc, Regex filter = null)
{
this.svc = svc;
this.filter = filter;
ts = svc.v1TaskScheduler;
wienum = ts?.Enum();
Reset();
}
/// <summary>Retrieves the current task. See <see cref="System.Collections.IEnumerator.Current"/> for more information.</summary>
public Task Current => new Task(svc, ICurrent);
object System.Collections.IEnumerator.Current => Current;
internal int Count
{
get
{
var i = 0;
Reset();
while (MoveNext())
i++;
Reset();
return i;
}
}
internal V1Interop.ITask ICurrent => TaskService.GetTask(ts, curItem);
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
if (wienum != null) Marshal.ReleaseComObject(wienum);
ts = null;
}
/// <summary>Moves to the next task. See MoveNext for more information.</summary>
/// <returns>true if next task found, false if no more tasks.</returns>
public bool MoveNext()
{
var names = IntPtr.Zero;
var valid = false;
do
{
curItem = null;
uint uFetched = 0;
try
{
wienum?.Next(1, out names, out uFetched);
if (uFetched != 1)
break;
using (var name = new V1Interop.CoTaskMemString(Marshal.ReadIntPtr(names)))
curItem = name.ToString();
if (curItem != null && curItem.EndsWith(".job", StringComparison.InvariantCultureIgnoreCase))
curItem = curItem.Remove(curItem.Length - 4);
}
catch { }
finally { Marshal.FreeCoTaskMem(names); names = IntPtr.Zero; }
// If name doesn't match filter, look for next item
if (filter != null && curItem != null)
{
if (!filter.IsMatch(curItem))
continue;
}
V1Interop.ITask itask = null;
try { itask = ICurrent; valid = true; }
catch { valid = false; }
finally { Marshal.ReleaseComObject(itask); }
} while (!valid);
return (curItem != null);
}
/// <summary>Reset task enumeration. See Reset for more information.</summary>
public void Reset()
{
curItem = null;
wienum?.Reset();
}
}
private class V2TaskEnumerator : ComEnumerator<Task, V2Interop.IRegisteredTask>
{
private readonly Regex filter;
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()
{
var hasNext = base.MoveNext();
while (hasNext)
{
if (filter == null || filter.IsMatch(iEnum?.Current?.Name ?? ""))
break;
hasNext = base.MoveNext();
}
return hasNext;
}
}
}
}

View File

@ -0,0 +1,879 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using JetBrains.Annotations;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Changes to tasks and the engine that cause events.
/// </summary>
public enum StandardTaskEventId
{
/// <summary>Task Scheduler started an instance of a task for a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348558(v=ws.10).aspx">Event ID 100</a> on TechNet.</remarks>
JobStart = 100,
/// <summary>Task Scheduler failed to start a task for a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315710(v=ws.10).aspx">Event ID 101</a> on TechNet.</remarks>
JobStartFailed = 101,
/// <summary>Task Scheduler successfully finished an instance of a task for a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348530(v=ws.10).aspx">Event ID 102</a> on TechNet.</remarks>
JobSuccess = 102,
/// <summary>Task Scheduler failed to start an instance of a task for a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315643(v=ws.10).aspx">Event ID 103</a> on TechNet.</remarks>
JobFailure = 103,
/// <summary>Task Scheduler failed to log on the user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727217(v=ws.10).aspx">Event ID 104</a> on TechNet.</remarks>
LogonFailure = 104,
/// <summary>Task Scheduler failed to impersonate a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348541(v=ws.10).aspx">Event ID 105</a> on TechNet.</remarks>
ImpersonationFailure = 105,
/// <summary>The a user registered the Task Scheduler a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363640(v=ws.10).aspx">Event ID 106</a> on TechNet.</remarks>
JobRegistered = 106,
/// <summary>Task Scheduler launched an instance of a task due to a time trigger.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348590(v=ws.10).aspx">Event ID 107</a> on TechNet.</remarks>
TimeTrigger = 107,
/// <summary>Task Scheduler launched an instance of a task due to an event trigger.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727031(v=ws.10).aspx">Event ID 108</a> on TechNet.</remarks>
EventTrigger = 108,
/// <summary>Task Scheduler launched an instance of a task due to a registration trigger.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363702(v=ws.10).aspx">Event ID 109</a> on TechNet.</remarks>
ImmediateTrigger = 109,
/// <summary>Task Scheduler launched an instance of a task for a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363721(v=ws.10).aspx">Event ID 110</a> on TechNet.</remarks>
Run = 110,
/// <summary>Task Scheduler terminated an instance of a task due to exceeding the time allocated for execution, as configured in the task definition.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315549(v=ws.10).aspx">Event ID 111</a> on TechNet.</remarks>
JobTermination = 111,
/// <summary>Task Scheduler could not start a task because the network was unavailable. Ensure the computer is connected to the required network as specified in the task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363779(v=ws.10).aspx">Event ID 112</a> on TechNet.</remarks>
JobNoStartWithoutNetwork = 112,
/// <summary>The Task Scheduler registered the a task, but not all the specified triggers will start the task. Ensure all the task triggers are valid.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315656(v=ws.10).aspx">Event ID 113</a> on TechNet.</remarks>
TaskRegisteredWithoutSomeTriggers = 113,
/// <summary>Task Scheduler could not launch a task as scheduled. Instance is started now as required by the configuration option to start the task when available, if the scheduled time is missed.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc775066(v=ws.10).aspx">Event ID 114</a> on TechNet.</remarks>
MissedTaskLaunched = 114,
/// <summary>Task Scheduler failed to roll back a transaction when updating or deleting a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315711(v=ws.10).aspx">Event ID 115</a> on TechNet.</remarks>
TransactionRollbackFailure = 115,
/// <summary>Task Scheduler saved the configuration for a task, but the credentials used to run the task could not be stored.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363682(v=ws.10).aspx">Event ID 116</a> on TechNet.</remarks>
TaskRegisteredWithoutCredentials = 116,
/// <summary>Task Scheduler launched an instance of a task due to an idle condition.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315755(v=ws.10).aspx">Event ID 117</a> on TechNet.</remarks>
IdleTrigger = 117,
/// <summary>Task Scheduler launched an instance of a task due to system startup.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc775107(v=ws.10).aspx">Event ID 118</a> on TechNet.</remarks>
BootTrigger = 118,
/// <summary>Task Scheduler launched an instance of a task due to a user logon.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315498(v=ws.10).aspx">Event ID 119</a> on TechNet.</remarks>
LogonTrigger = 119,
/// <summary>Task Scheduler launched an instance of a task due to a user connecting to the console.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315609(v=ws.10).aspx">Event ID 120</a> on TechNet.</remarks>
ConsoleConnectTrigger = 120,
/// <summary>Task Scheduler launched an instance of a task due to a user disconnecting from the console.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363748(v=ws.10).aspx">Event ID 121</a> on TechNet.</remarks>
ConsoleDisconnectTrigger = 121,
/// <summary>Task Scheduler launched an instance of a task due to a user remotely connecting.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc774994(v=ws.10).aspx">Event ID 122</a> on TechNet.</remarks>
RemoteConnectTrigger = 122,
/// <summary>Task Scheduler launched an instance of a task due to a user remotely disconnecting.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc775034(v=ws.10).aspx">Event ID 123</a> on TechNet.</remarks>
RemoteDisconnectTrigger = 123,
/// <summary>Task Scheduler launched an instance of a task due to a user locking the computer.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315499(v=ws.10).aspx">Event ID 124</a> on TechNet.</remarks>
SessionLockTrigger = 124,
/// <summary>Task Scheduler launched an instance of a task due to a user unlocking the computer.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727048(v=ws.10).aspx">Event ID 125</a> on TechNet.</remarks>
SessionUnlockTrigger = 125,
/// <summary>Task Scheduler failed to execute a task. Task Scheduler is attempting to restart the task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363647(v=ws.10).aspx">Event ID 126</a> on TechNet.</remarks>
FailedTaskRestart = 126,
/// <summary>Task Scheduler failed to execute a task due to a shutdown race condition. Task Scheduler is attempting to restart the task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc726976(v=ws.10).aspx">Event ID 127</a> on TechNet.</remarks>
RejectedTaskRestart = 127,
/// <summary>Task Scheduler did not launch a task because the current time exceeds the configured task end time.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315638(v=ws.10).aspx">Event ID 128</a> on TechNet.</remarks>
IgnoredTaskStart = 128,
/// <summary>Task Scheduler launched an instance of a task in a new process.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315544(v=ws.10).aspx">Event ID 129</a> on TechNet.</remarks>
CreatedTaskProcess = 129,
/// <summary>The Task Scheduler service failed to start a task due to the service being busy.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315521(v=ws.10).aspx">Event ID 130</a> on TechNet.</remarks>
TaskNotRunServiceBusy = 130,
/// <summary>Task Scheduler failed to start a task because the number of tasks in the task queue exceeds the quota currently configured.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363733(v=ws.10).aspx">Event ID 131</a> on TechNet.</remarks>
TaskNotStartedTaskQueueQuotaExceeded = 131,
/// <summary>The Task Scheduler task launching queue quota is approaching its preset limit of tasks currently configured.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363705(v=ws.10).aspx">Event ID 132</a> on TechNet.</remarks>
TaskQueueQuotaApproaching = 132,
/// <summary>Task Scheduler failed to start a task in the task engine for a user.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315645(v=ws.10).aspx">Event ID 133</a> on TechNet.</remarks>
TaskNotStartedEngineQuotaExceeded = 133,
/// <summary>Task Engine for a user is approaching its preset limit of tasks.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc774865(v=ws.10).aspx">Event ID 134</a> on TechNet.</remarks>
EngineQuotaApproaching = 134,
/// <summary>Task Scheduler did not launch a task because launch condition not met, machine not idle.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363654(v=ws.10).aspx">Event ID 135</a> on TechNet.</remarks>
NotStartedWithoutIdle = 135,
/// <summary>A user updated Task Scheduler a task</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363717(v=ws.10).aspx">Event ID 140</a> on TechNet.</remarks>
TaskUpdated = 140,
/// <summary>A user deleted Task Scheduler a task</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348535(v=ws.10).aspx">Event ID 141</a> on TechNet.</remarks>
TaskDeleted = 141,
/// <summary>A user disabled Task Scheduler a task</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363727(v=ws.10).aspx">Event ID 142</a> on TechNet.</remarks>
TaskDisabled = 142,
/// <summary>Task Scheduler woke up the computer to run a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc775065(v=ws.10).aspx">Event ID 145</a> on TechNet.</remarks>
TaskStartedOnComputerWakeup = 145,
/// <summary>Task Scheduler failed to subscribe the event trigger for a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315495(v=ws.10).aspx">Event ID 150</a> on TechNet.</remarks>
TaskEventSubscriptionFailed = 150,
/// <summary>Task Scheduler launched an action in an instance of a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc775088(v=ws.10).aspx">Event ID 200</a> on TechNet.</remarks>
ActionStart = 200,
/// <summary>Task Scheduler successfully completed a task instance and action.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348568(v=ws.10).aspx">Event ID 201</a> on TechNet.</remarks>
ActionSuccess = 201,
/// <summary>Task Scheduler failed to complete an instance of a task with an action.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315592(v=ws.10).aspx">Event ID 202</a> on TechNet.</remarks>
ActionFailure = 202,
/// <summary>Task Scheduler failed to launch an action in a task instance.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363729(v=ws.10).aspx">Event ID 203</a> on TechNet.</remarks>
ActionLaunchFailure = 203,
/// <summary>Task Scheduler failed to retrieve the event triggering values for a task . The event will be ignored.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348579(v=ws.10).aspx">Event ID 204</a> on TechNet.</remarks>
EventRenderFailed = 204,
/// <summary>Task Scheduler failed to match the pattern of events for a task. The events will be ignored.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363661(v=ws.10).aspx">Event ID 205</a> on TechNet.</remarks>
EventAggregateFailed = 205,
/// <summary>Task Scheduler is shutting down the a task engine.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348583(v=ws.10).aspx">Event ID 301</a> on TechNet.</remarks>
SessionExit = 301,
/// <summary>Task Scheduler is shutting down the a task engine due to an error. </summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727080(v=ws.10).aspx">Event ID 303</a> on TechNet.</remarks>
SessionError = 303,
/// <summary>Task Scheduler sent a task to a task engine.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315534(v=ws.10).aspx">Event ID 304</a> on TechNet.</remarks>
SessionSentJob = 304,
/// <summary>Task Scheduler did not send a task to a task engine.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363776(v=ws.10).aspx">Event ID 305</a> on TechNet.</remarks>
SessionSentJobFailed = 305,
/// <summary>For a Task Scheduler task engine, the thread pool failed to process the message.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315525(v=ws.10).aspx">Event ID 306</a> on TechNet.</remarks>
SessionFailedToProcessMessage = 306,
/// <summary>The Task Scheduler service failed to connect to a task engine process.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315605(v=ws.10).aspx">Event ID 307</a> on TechNet.</remarks>
SessionManagerConnectFailed = 307,
/// <summary>Task Scheduler connected to a task engine process.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315746(v=ws.10).aspx">Event ID 308</a> on TechNet.</remarks>
SessionConnected = 308,
/// <summary>There are Task Scheduler tasks orphaned during a task engine shutdown.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348580(v=ws.10).aspx">Event ID 309</a> on TechNet.</remarks>
SessionJobsOrphaned = 309,
/// <summary>Task Scheduler started a task engine process.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315668(v=ws.10).aspx">Event ID 310</a> on TechNet.</remarks>
SessionProcessStarted = 310,
/// <summary>Task Scheduler failed to start a task engine process due to an error.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363691(v=ws.10).aspx">Event ID 311</a> on TechNet.</remarks>
SessionProcessLaunchFailed = 311,
/// <summary>Task Scheduler created the Win32 job object for a task engine.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc775071(v=ws.10).aspx">Event ID 312</a> on TechNet.</remarks>
SessionWin32ObjectCreated = 312,
/// <summary>The Task Scheduler channel is ready to send and receive messages.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363685(v=ws.10).aspx">Event ID 313</a> on TechNet.</remarks>
SessionChannelReady = 313,
/// <summary>Task Scheduler has no tasks running for a task engine, and the idle timer has started.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315517(v=ws.10).aspx">Event ID 314</a> on TechNet.</remarks>
SessionIdle = 314,
/// <summary>A task engine process failed to connect to the Task Scheduler service.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363798(v=ws.10).aspx">Event ID 315</a> on TechNet.</remarks>
SessionProcessConnectFailed = 315,
/// <summary>A task engine failed to send a message to the Task Scheduler service.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315748(v=ws.10).aspx">Event ID 316</a> on TechNet.</remarks>
SessionMessageSendFailed = 316,
/// <summary>Task Scheduler started a task engine process.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315663(v=ws.10).aspx">Event ID 317</a> on TechNet.</remarks>
SessionProcessMainStarted = 317,
/// <summary>Task Scheduler shut down a task engine process.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727204(v=ws.10).aspx">Event ID 318</a> on TechNet.</remarks>
SessionProcessMainShutdown = 318,
/// <summary>A task engine received a message from the Task Scheduler service requesting to launch a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363712(v=ws.10).aspx">Event ID 319</a> on TechNet.</remarks>
SessionProcessReceivedStartJob = 319,
/// <summary>A task engine received a message from the Task Scheduler service requesting to stop a task instance.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc774993(v=ws.10).aspx">Event ID 320</a> on TechNet.</remarks>
SessionProcessReceivedStopJob = 320,
/// <summary>Task Scheduler did not launch a task because an instance of the same task is already running.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315618(v=ws.10).aspx">Event ID 322</a> on TechNet.</remarks>
NewInstanceIgnored = 322,
/// <summary>Task Scheduler stopped an instance of a task in order to launch a new instance.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315542(v=ws.10).aspx">Event ID 323</a> on TechNet.</remarks>
RunningInstanceStopped = 323,
/// <summary>Task Scheduler queued an instance of a task and will launch it as soon as another instance completes.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363759(v=ws.10).aspx">Event ID 324</a> on TechNet.</remarks>
NewInstanceQueued = 324,
/// <summary>Task Scheduler queued an instance of a task that will launch immediately.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363654(v=ws.10).aspx">Event ID 325</a> on TechNet.</remarks>
InstanceQueued = 325,
/// <summary>Task Scheduler did not launch a task because the computer is running on batteries. If launching the task on batteries is required, change the respective flag in the task configuration.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315644(v=ws.10).aspx">Event ID 326</a> on TechNet.</remarks>
NoStartOnBatteries = 326,
/// <summary>Task Scheduler stopped an instance of a task because the computer is switching to battery power.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348611(v=ws.10).aspx">Event ID 327</a> on TechNet.</remarks>
StoppingOnBatteries = 327,
/// <summary>Task Scheduler stopped an instance of a task because the computer is no longer idle.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363732(v=ws.10).aspx">Event ID 328</a> on TechNet.</remarks>
StoppingOffIdle = 328,
/// <summary>Task Scheduler stopped an instance of a task because the task timed out.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348536(v=ws.10).aspx">Event ID 329</a> on TechNet.</remarks>
StoppingOnTimeout = 329,
/// <summary>Task Scheduler stopped an instance of a task as request by a user .</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348610(v=ws.10).aspx">Event ID 330</a> on TechNet.</remarks>
StoppingOnRequest = 330,
/// <summary>Task Scheduler will continue to execute an instance of a task even after the designated timeout, due to a failure to create the timeout mechanism.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315673(v=ws.10).aspx">Event ID 331</a> on TechNet.</remarks>
TimeoutWontWork = 331,
/// <summary>Task Scheduler did not launch a task because a user was not logged on when the launching conditions were met. Ensure the user is logged on or change the task definition to allow the task to launch when the user is logged off.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315612(v=ws.10).aspx">Event ID 332</a> on TechNet.</remarks>
NoStartUserNotLoggedOn = 332,
/// <summary>The Task Scheduler service has started.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315626(v=ws.10).aspx">Event ID 400</a> on TechNet.</remarks>
ScheduleServiceStart = 400,
/// <summary>The Task Scheduler service failed to start due to an error.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363598(v=ws.10).aspx">Event ID 401</a> on TechNet.</remarks>
ScheduleServiceStartFailed = 401,
/// <summary>Task Scheduler service is shutting down.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363724(v=ws.10).aspx">Event ID 402</a> on TechNet.</remarks>
ScheduleServiceStop = 402,
/// <summary>The Task Scheduler service has encountered an error.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315744(v=ws.10).aspx">Event ID 403</a> on TechNet.</remarks>
ScheduleServiceError = 403,
/// <summary>The Task Scheduler service has encountered an RPC initialization error.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363713(v=ws.10).aspx">Event ID 404</a> on TechNet.</remarks>
ScheduleServiceRpcInitError = 404,
/// <summary>The Task Scheduler service has failed to initialize COM.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363728(v=ws.10).aspx">Event ID 405</a> on TechNet.</remarks>
ScheduleServiceComInitError = 405,
/// <summary>The Task Scheduler service failed to initialize the credentials store.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315727(v=ws.10).aspx">Event ID 406</a> on TechNet.</remarks>
ScheduleServiceCredStoreInitError = 406,
/// <summary>Task Scheduler service failed to initialize LSA.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315580(v=ws.10).aspx">Event ID 407</a> on TechNet.</remarks>
ScheduleServiceLsaInitError = 407,
/// <summary>Task Scheduler service failed to initialize idle state detection module. Idle tasks may not be started as required.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315523(v=ws.10).aspx">Event ID 408</a> on TechNet.</remarks>
ScheduleServiceIdleServiceInitError = 408,
/// <summary>The Task Scheduler service failed to initialize a time change notification. System time updates may not be picked by the service and task schedules may not be updated.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc774954(v=ws.10).aspx">Event ID 409</a> on TechNet.</remarks>
ScheduleServiceTimeChangeInitError = 409,
/// <summary>Task Scheduler service received a time system change notification.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315633(v=ws.10).aspx">Event ID 411</a> on TechNet.</remarks>
ScheduleServiceTimeChangeSignaled = 411,
/// <summary>Task Scheduler service failed to launch tasks triggered by computer startup. Restart the Task Scheduler service.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315637(v=ws.10).aspx">Event ID 412</a> on TechNet.</remarks>
ScheduleServiceRunBootJobsFailed = 412,
/// <summary>Task Scheduler service started Task Compatibility module.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727087(v=ws.10).aspx">Event ID 700</a> on TechNet.</remarks>
CompatStart = 700,
/// <summary>Task Scheduler service failed to start Task Compatibility module. Tasks may not be able to register on previous Window versions.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315530(v=ws.10).aspx">Event ID 701</a> on TechNet.</remarks>
CompatStartFailed = 701,
/// <summary>Task Scheduler failed to initialize the RPC server for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315721(v=ws.10).aspx">Event ID 702</a> on TechNet.</remarks>
CompatStartRpcFailed = 702,
/// <summary>Task Scheduler failed to initialize Net Schedule API for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348604(v=ws.10).aspx">Event ID 703</a> on TechNet.</remarks>
CompatStartNetscheduleFailed = 703,
/// <summary>Task Scheduler failed to initialize LSA for starting the Task Compatibility module. Tasks may not be able to register on previous Window versions.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315572(v=ws.10).aspx">Event ID 704</a> on TechNet.</remarks>
CompatStartLsaFailed = 704,
/// <summary>Task Scheduler failed to start directory monitoring for the Task Compatibility module.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/cc727147(v=ws.10).aspx">Event ID 705</a> on TechNet.</remarks>
CompatDirectoryMonitorFailed = 705,
/// <summary>Task Compatibility module failed to update a task to the required status.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315682(v=ws.10).aspx">Event ID 706</a> on TechNet.</remarks>
CompatTaskStatusUpdateFailed = 706,
/// <summary>Task Compatibility module failed to delete a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348545(v=ws.10).aspx">Event ID 707</a> on TechNet.</remarks>
CompatTaskDeleteFailed = 707,
/// <summary>Task Compatibility module failed to set a security descriptor for a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315719(v=ws.10).aspx">Event ID 708</a> on TechNet.</remarks>
CompatTaskSetSdFailed = 708,
/// <summary>Task Compatibility module failed to update a task.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363614(v=ws.10).aspx">Event ID 709</a> on TechNet.</remarks>
CompatTaskUpdateFailed = 709,
/// <summary>Task Compatibility module failed to upgrade existing tasks. Upgrade will be attempted again next time 'Task Scheduler' service starts.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363608(v=ws.10).aspx">Event ID 710</a> on TechNet.</remarks>
CompatUpgradeStartFailed = 710,
/// <summary>Task Compatibility module failed to upgrade NetSchedule account.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348554(v=ws.10).aspx">Event ID 711</a> on TechNet.</remarks>
CompatUpgradeNsAccountFailed = 711,
/// <summary>Task Compatibility module failed to read existing store to upgrade tasks.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315519(v=ws.10).aspx">Event ID 712</a> on TechNet.</remarks>
CompatUpgradeStoreEnumFailed = 712,
/// <summary>Task Compatibility module failed to load a task for upgrade.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315728(v=ws.10).aspx">Event ID 713</a> on TechNet.</remarks>
CompatUpgradeTaskLoadFailed = 713,
/// <summary>Task Compatibility module failed to register a task for upgrade.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd363701(v=ws.10).aspx">Event ID 714</a> on TechNet.</remarks>
CompatUpgradeTaskRegistrationFailed = 714,
/// <summary>Task Compatibility module failed to delete LSA store for upgrade.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348581(v=ws.10).aspx">Event ID 715</a> on TechNet.</remarks>
CompatUpgradeLsaCleanupFailed = 715,
/// <summary>Task Compatibility module failed to upgrade existing scheduled tasks.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315624(v=ws.10).aspx">Event ID 716</a> on TechNet.</remarks>
CompatUpgradeFailed = 716,
/// <summary>Task Compatibility module failed to determine if upgrade is needed.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd315731(v=ws.10).aspx">Event ID 717</a> on TechNet.</remarks>
CompatUpgradeNeedNotDetermined = 717,
/// <summary>Task scheduler was unable to upgrade the credential store from the Beta 2 version. You may need to re-register any tasks that require passwords.</summary>
/// <remarks>For detailed information, see the documentation for <a href="https://technet.microsoft.com/en-us/library/dd348576(v=ws.10).aspx">Event ID 718</a> on TechNet.</remarks>
VistaBeta2CredstoreUpgradeFailed = 718,
/// <summary>A unknown value.</summary>
Unknown = -2
}
/// <summary>
/// Historical event information for a task. This class wraps and extends the <see cref="EventRecord"/> class.
/// </summary>
/// <remarks>
/// For events on systems prior to Windows Vista, this class will only have information for the TaskPath, TimeCreated and EventId properties.
/// </remarks>
[PublicAPI]
public sealed class TaskEvent : IComparable<TaskEvent>
{
internal TaskEvent([NotNull] EventRecord rec)
{
EventId = rec.Id;
EventRecord = rec;
Version = rec.Version;
TaskCategory = rec.TaskDisplayName;
OpCode = rec.OpcodeDisplayName;
TimeCreated = rec.TimeCreated;
RecordId = rec.RecordId;
ActivityId = rec.ActivityId;
Level = rec.LevelDisplayName;
UserId = rec.UserId;
ProcessId = rec.ProcessId;
TaskPath = rec.Properties.Count > 0 ? rec.Properties[0]?.Value?.ToString() : null;
DataValues = new EventDataValues(rec as EventLogRecord);
}
internal TaskEvent([NotNull] string taskPath, StandardTaskEventId id, DateTime time)
{
EventId = (int)id;
TaskPath = taskPath;
TimeCreated = time;
}
/// <summary>
/// Gets the activity id. This value is <c>null</c> for V1 events.
/// </summary>
public Guid? ActivityId { get; internal set; }
/// <summary>
/// An indexer that gets the value of each of the data item values. This value is <c>null</c> for V1 events.
/// </summary>
/// <value>
/// The data values.
/// </value>
public EventDataValues DataValues { get; }
/// <summary>
/// Gets the event id.
/// </summary>
public int EventId { get; internal set; }
/// <summary>
/// Gets the underlying <see cref="EventRecord"/>. This value is <c>null</c> for V1 events.
/// </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>
public string Level { get; internal set; }
/// <summary>
/// Gets the op code. This value is <c>null</c> for V1 events.
/// </summary>
public string OpCode { get; internal set; }
/// <summary>
/// Gets the process id. This value is <c>null</c> for V1 events.
/// </summary>
public int? ProcessId { get; internal set; }
/// <summary>
/// Gets the record id. This value is <c>null</c> for V1 events.
/// </summary>
public long? RecordId { get; internal set; }
/// <summary>
/// Gets the task category. This value is <c>null</c> for V1 events.
/// </summary>
public string TaskCategory { get; internal set; }
/// <summary>
/// Gets the task path.
/// </summary>
public string TaskPath { get; internal set; }
/// <summary>
/// Gets the time created.
/// </summary>
public DateTime? TimeCreated { get; internal set; }
/// <summary>
/// Gets the user id. This value is <c>null</c> for V1 events.
/// </summary>
public System.Security.Principal.SecurityIdentifier UserId { get; internal set; }
/// <summary>
/// Gets the version. This value is <c>null</c> for V1 events.
/// </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>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString() => EventRecord?.FormatDescription() ?? TaskPath;
/// <summary>
/// Compares the current object with another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the other parameter.Zero This object is equal to other. Greater than zero This object is greater than other.
/// </returns>
public int CompareTo(TaskEvent other)
{
int i = string.Compare(TaskPath, other.TaskPath, StringComparison.Ordinal);
if (i == 0 && EventRecord != null)
{
i = string.Compare(ActivityId.ToString(), other.ActivityId.ToString(), StringComparison.Ordinal);
if (i == 0)
i = Convert.ToInt32(RecordId - other.RecordId);
}
return i;
}
/// <summary>
/// Get indexer class for <see cref="EventLogRecord"/> data values.
/// </summary>
public class EventDataValues
{
private readonly EventLogRecord rec;
internal EventDataValues(EventLogRecord eventRec)
{
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;
}
}
}
}
/// <summary>
/// An enumerator over a task's history of events.
/// </summary>
public sealed class TaskEventEnumerator : IEnumerator<TaskEvent>
{
private EventRecord curRec;
private EventLogReader log;
internal TaskEventEnumerator([NotNull] EventLogReader log)
{
this.log = log;
}
/// <summary>
/// Gets the element in the collection at the current position of the enumerator.
/// </summary>
/// <returns>
/// The element in the collection at the current position of the enumerator.
/// </returns>
public TaskEvent Current => new TaskEvent(curRec);
/// <summary>
/// Gets the element in the collection at the current position of the enumerator.
/// </summary>
/// <returns>
/// The element in the collection at the current position of the enumerator.
/// </returns>
object System.Collections.IEnumerator.Current => Current;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
log.CancelReading();
log.Dispose();
log = null;
}
/// <summary>
/// Advances the enumerator to the next element of the collection.
/// </summary>
/// <returns>
/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
/// </returns>
/// <exception cref="T:System.InvalidOperationException">
/// The collection was modified after the enumerator was created.
/// </exception>
public bool MoveNext() => (curRec = log.ReadEvent()) != null;
/// <summary>
/// Sets the enumerator to its initial position, which is before the first element in the collection.
/// </summary>
/// <exception cref="T:System.InvalidOperationException">
/// The collection was modified after the enumerator was created.
/// </exception>
public void Reset()
{
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>
/// Historical event log for a task. Only available for Windows Vista and Windows Server 2008 and later systems.
/// </summary>
/// <remarks>Many applications have the need to audit the execution of the tasks they supply. To enable this, the library provides the TaskEventLog class that allows for TaskEvent instances to be enumerated. This can be done for single tasks or the entire system. It can also be filtered by specific events or criticality.</remarks>
/// <example><code lang="cs"><![CDATA[
/// // Create a log instance for the Maint task in the root directory
/// TaskEventLog log = new TaskEventLog(@"\Maint",
/// // Specify the event id(s) you want to enumerate
/// new int[] { 141 /* TaskDeleted */, 201 /* ActionSuccess */ },
/// // Specify the start date of the events to enumerate. Here, we look at the last week.
/// DateTime.Now.AddDays(-7));
///
/// // Tell the enumerator to expose events 'newest first'
/// log.EnumerateInReverse = false;
///
/// // Enumerate the events
/// foreach (TaskEvent ev in log)
/// {
/// // TaskEvents can interpret event ids into a well known, readable, enumerated type
/// if (ev.StandardEventId == StandardTaskEventId.TaskDeleted)
/// output.WriteLine($" Task '{ev.TaskPath}' was deleted");
///
/// // TaskEvent exposes a number of properties regarding the event
/// else if (ev.EventId == 201)
/// output.WriteLine($" Completed action '{ev.DataValues["ActionName"]}',
/// ({ev.DataValues["ResultCode"]}) at {ev.TimeCreated.Value}.");
/// }
/// ]]></code></example>
public sealed class TaskEventLog : IEnumerable<TaskEvent>
{
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>
/// <param name="machineName">Name of the machine.</param>
/// <param name="taskPath">The task path. This can be retrieved using the <see cref="Task.Path" /> property.</param>
/// <param name="domain">The domain.</param>
/// <param name="user">The user.</param>
/// <param name="password">The password.</param>
/// <exception cref="NotSupportedException">Thrown when instantiated on an OS prior to Windows Vista.</exception>
public TaskEventLog([NotNull] string machineName, [CanBeNull] string taskPath, string domain = null, string user = null, string password = null)
{
Initialize(machineName, BuildQuery(taskPath), true, domain, user, password);
}
/// <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>
/// <param name="taskName">Name of the task.</param>
/// <param name="eventIDs">The event ids.</param>
/// <param name="levels">The levels.</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, int[] levels = null, DateTime? startTime = null, string machineName = null, string domain = null, string user = null, string password = null)
{
Initialize(machineName, BuildQuery(taskName, eventIDs, startTime, levels), true, domain, user, password);
}
internal static string BuildQuery(string taskName = null, int[] eventIDs = null, DateTime? startTime = null, int[] levels = null)
{
const string queryString =
"<QueryList>" +
" <Query Id=\"0\" Path=\"" + TSEventLogPath + "\">" +
" <Select Path=\"" + TSEventLogPath + "\">{0}</Select>" +
" </Query>" +
"</QueryList>";
const string OR = " or ";
const string AND = " and ";
System.Text.StringBuilder sb = new System.Text.StringBuilder("*");
if (eventIDs != null && eventIDs.Length > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(eventIDs, i => $"EventID={i}")));
}
if (levels != null && levels.Length > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(levels, i => $"Level={i}")));
}
if (startTime.HasValue)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("TimeCreated[@SystemTime>='{0}']", System.Xml.XmlConvert.ToString(startTime.Value, System.Xml.XmlDateTimeSerializationMode.RoundtripKind));
}
if (sb.Length > 1)
{
sb.Insert(1, "[System[Provider[@Name='Microsoft-Windows-TaskScheduler'] and ");
sb.Append(']');
}
if (!string.IsNullOrEmpty(taskName))
{
if (sb.Length == 1)
sb.Append('[');
else
sb.Append("]" + AND + "*[");
sb.AppendFormat("EventData[Data[@Name='TaskName']='{0}']", taskName);
}
if (sb.Length > 1)
sb.Append(']');
return string.Format(queryString, sb);
}
private void Initialize(string machineName, string query, bool revDir, string domain = null, string user = null, string password = null)
{
if (!IsVistaOrLater)
throw new NotSupportedException("Enumeration of task history not available on systems prior to Windows Vista and Windows Server 2008.");
System.Security.SecureString spwd = null;
if (password != null)
{
spwd = new System.Security.SecureString();
foreach (char c in password)
spwd.AppendChar(c);
}
Query = new EventLogQuery(TSEventLogPath, PathType.LogName, query) { ReverseDirection = revDir };
if (machineName != null && machineName != "." && !machineName.Equals(Environment.MachineName, StringComparison.InvariantCultureIgnoreCase))
Query.Session = new EventLogSession(machineName, domain, user, spwd, SessionAuthentication.Default);
}
/// <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>
/// <value>
/// <c>true</c> if enumerates in reverse (newest to oldest) by default; otherwise, <c>false</c> to enumerate oldest to newest.
/// </value>
[System.ComponentModel.DefaultValue(false)]
public bool EnumerateInReverse { get; set; }
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
IEnumerator<TaskEvent> IEnumerable<TaskEvent>.GetEnumerator() => GetEnumerator(EnumerateInReverse);
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <param name="reverse">if set to <c>true</c> reverse.</param>
/// <returns>
/// A <see cref="TaskEventEnumerator" /> that can be used to iterate through the collection.
/// </returns>
[NotNull]
public TaskEventEnumerator GetEnumerator(bool reverse)
{
Query.ReverseDirection = !reverse;
return new TaskEventEnumerator(new EventLogReader(Query));
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(false);
internal EventLogQuery Query { get; private set; }
}
}

View File

@ -0,0 +1,815 @@
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using JetBrains.Annotations;
namespace Microsoft.Win32.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>
/// If possible, gets the task associated with this event.
/// </summary>
/// <value>
/// The task or <c>null</c> if unable to retrieve.
/// </value>
public Task Task
{
get { try { return taskService?.GetTask(TaskPath); } catch { return null; } }
}
/// <summary>
/// Gets the <see cref="TaskEvent"/>.
/// </summary>
/// <value>
/// The TaskEvent.
/// </value>
[NotNull]
public TaskEvent TaskEvent { get; }
/// <summary>
/// Gets the task name.
/// </summary>
/// <value>
/// The task name.
/// </value>
public string TaskName => Path.GetFileName(TaskPath);
/// <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);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher"/> class watching only
/// those events for the specified task.
/// </summary>
/// <param name="task">The task to watch.</param>
/// <exception cref="ArgumentNullException">Occurs if the <paramref name="task"/> is <c>null</c>.</exception>
public TaskEventWatcher([NotNull] Task task) : this(task?.TaskService)
{
if (task == null)
throw new ArgumentNullException(nameof(task));
InitTask(task);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher"/> class watching only those events for
/// the tasks whose name matches the <paramref name="taskFilter"/> in the specified <paramref name="taskFolder"/>
/// and optionally all subfolders.
/// </summary>
/// <param name="taskFolder">The task folder to watch.</param>
/// <param name="taskFilter">The filter for task names using standard file system wildcards. Use "*" to include all tasks.</param>
/// <param name="includeSubfolders">if set to <c>true</c> include events from tasks subfolders.</param>
/// <exception cref="ArgumentNullException">Occurs if the <paramref name="taskFolder"/> is <c>null</c>.</exception>
public TaskEventWatcher([NotNull] TaskFolder taskFolder, string taskFilter = "*", bool includeSubfolders = false) : this(taskFolder?.TaskService)
{
if (taskFolder == null)
throw new ArgumentNullException(nameof(taskFolder));
InitTask(taskFolder, taskFilter, includeSubfolders);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher" /> class.
/// </summary>
/// <param name="folder">The task folder to watch.</param>
/// <param name="taskFilter">The filter for task names using standard file system wildcards. Use "*" to include all tasks.</param>
/// <param name="includeSubfolders">if set to <c>true</c> include events from tasks subfolders.</param>
/// <param name="taskService">The task service.</param>
public TaskEventWatcher(string folder, string taskFilter, bool includeSubfolders, TaskService taskService = null) : this(taskService ?? TaskService.Instance)
{
InitTask(folder, taskFilter, includeSubfolders);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher" /> class on a remote machine.
/// </summary>
/// <param name="machineName">Name of the remote machine.</param>
/// <param name="taskPath">The task path.</param>
/// <param name="domain">The domain of the user account.</param>
/// <param name="user">The user name with permissions on the remote machine.</param>
/// <param name="password">The password for the user.</param>
public TaskEventWatcher(string machineName, string taskPath, string domain = null, string user = null, string password = null) : this(new TaskService(machineName, user, domain, password))
{
InitTask(taskPath);
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskEventWatcher" /> class on a remote machine.
/// </summary>
/// <param name="machineName">Name of the remote machine.</param>
/// <param name="folder">The task folder to watch.</param>
/// <param name="taskFilter">The filter for task names using standard file system wildcards. Use "*" to include all tasks.</param>
/// <param name="includeSubfolders">if set to <c>true</c> include events from tasks subfolders.</param>
/// <param name="domain">The domain of the user account.</param>
/// <param name="user">The user name with permissions on the remote machine.</param>
/// <param name="password">The password for the user.</param>
public TaskEventWatcher(string machineName, string folder, string taskFilter = "*", bool includeSubfolders = false, string domain = null, string user = null, string password = null) : this(new TaskService(machineName, user, domain, password))
{
InitTask(folder, taskFilter, includeSubfolders);
}
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="Microsoft.Win32.TaskScheduler.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([NotNull] Task task)
{
Filter.TaskName = task.Name;
Folder = task.Folder.Path;
}
private void InitTask(TaskFolder taskFolder, string taskFilter, bool includeSubfolders)
{
this.includeSubfolders = includeSubfolders;
Filter.TaskName = taskFilter;
Folder = taskFolder?.Path;
}
private void InitTask(string taskFolder, string taskFilter, bool includeSubfolders)
{
this.includeSubfolders = includeSubfolders;
Filter.TaskName = taskFilter;
Folder = taskFolder;
}
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 ResetTaskService() { ts = TaskService.Instance; }
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 bool ShouldSerializeFilter() => Filter.ShouldSerialize();
private bool ShouldSerializeTaskService() => !Equals(TaskService, TaskService.Instance);
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");
internal bool ShouldSerialize() => ids != null || levels != null || filter != star;
}
}
#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

@ -0,0 +1,633 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.AccessControl;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Microsoft.Win32.TaskScheduler.V1Interop;
using Microsoft.Win32.TaskScheduler.V2Interop;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Provides the methods that are used to register (create) tasks in the folder, remove tasks from the folder, and create or remove subfolders from the folder.
/// </summary>
[PublicAPI]
public sealed class TaskFolder : IDisposable, IComparable<TaskFolder>
{
private ITaskScheduler v1List;
private readonly ITaskFolder v2Folder;
internal const string rootString = @"\";
internal TaskFolder([NotNull] TaskService svc)
{
TaskService = svc;
v1List = svc.v1TaskScheduler;
}
internal TaskFolder([NotNull] TaskService svc, [NotNull] ITaskFolder iFldr)
{
TaskService = svc;
v2Folder = iFldr;
}
/// <summary>
/// Releases all resources used by this class.
/// </summary>
public void Dispose()
{
if (v2Folder != null)
Marshal.ReleaseComObject(v2Folder);
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>
[NotNull, ItemNotNull]
public TaskFolderCollection SubFolders
{
get
{
try
{
if (v2Folder != null)
return new TaskFolderCollection(this, v2Folder.GetFolders(0));
} catch { }
return new TaskFolderCollection();
}
}
/// <summary>
/// Gets a collection of all the tasks in the folder.
/// </summary>
[NotNull, ItemNotNull]
public TaskCollection Tasks => GetTasks();
/// <summary>
/// Gets or sets the <see cref="TaskService"/> that manages this task.
/// </summary>
/// <value>The task service.</value>
public TaskService TaskService { get; }
/// <summary>
/// Compares the current object with another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other" /> parameter.Zero This object is equal to <paramref name="other" />. Greater than zero This object is greater than <paramref name="other" />.
/// </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>
/// <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="sddlForm">The security descriptor associated with the folder.</param>
/// <param name="exceptionOnExists">Set this value to false to avoid having an exception called if the folder already exists.</param>
/// <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="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();
ITaskFolder ifld = null;
try { ifld = v2Folder.CreateFolder(subFolderName, sddlForm); }
catch (COMException ce)
{
int serr = ce.ErrorCode & 0x0000FFFF;
if (serr == 0xb7) // ERROR_ALREADY_EXISTS
{
if (exceptionOnExists) throw;
try
{
ifld = v2Folder.GetFolder(subFolderName);
if (ifld != null && sddlForm != null && sddlForm.Trim().Length > 0)
{
string sd = ifld.GetSecurityDescriptor((int)Task.defaultSecurityInfosSections);
if (string.Compare(sddlForm, sd, StringComparison.OrdinalIgnoreCase) != 0)
throw new SecurityException("Security descriptor mismatch between specified credentials and credentials on existing folder by same name.");
}
}
catch
{
if (ifld != null)
Marshal.ReleaseComObject(ifld);
throw;
}
}
else if (serr == 0x534 || serr == 0x538 || serr == 0x539 || serr == 0x53A || serr == 0x519 || serr == 0x57)
throw new ArgumentException(@"Invalid SDDL form", nameof(sddlForm), ce);
else
throw;
}
return new TaskFolder(TaskService, ifld);
}
/// <summary>
/// Deletes a subfolder from the parent folder. Not available to Task Scheduler 1.0.
/// </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="Microsoft.Win32.TaskScheduler.NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
public void DeleteFolder([NotNull] string subFolderName, bool exceptionOnNotExists = true)
{
if (v2Folder != null)
{
try
{
v2Folder.DeleteFolder(subFolderName, 0);
}
catch (Exception e)
{
if (!(e is FileNotFoundException || e is DirectoryNotFoundException) || exceptionOnNotExists)
throw;
}
}
else
throw new NotV1SupportedException();
}
/// <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>
public override bool Equals(object obj)
{
var folder = obj as TaskFolder;
if (folder != null)
return Path == folder.Path && TaskService.TargetServer == folder.TaskService.TargetServer && GetSecurityDescriptorSddlForm() == folder.GetSecurityDescriptorSddlForm();
return false;
}
/// <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>
/// <param name="includeSections">Section(s) of the security descriptor to return.</param>
/// <returns>The security descriptor for the folder.</returns>
/// <exception cref="NotV1SupportedException">Not supported under Task Scheduler 1.0.</exception>
public string GetSecurityDescriptorSddlForm(SecurityInfos includeSections = Task.defaultSecurityInfosSections)
{
if (v2Folder != null)
return v2Folder.GetSecurityDescriptor((int)includeSections);
throw new NotV1SupportedException();
}
/// <summary>
/// Gets a collection of all the tasks in the folder whose name matches the optional <paramref name="filter"/>.
/// </summary>
/// <param name="filter">The optional name filter expression.</param>
/// <returns>Collection of all matching tasks.</returns>
[NotNull, ItemNotNull]
public TaskCollection GetTasks(Regex filter = null)
{
if (v2Folder != null)
return new TaskCollection(this, v2Folder.GetTasks(1), filter);
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>
/// <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="definition">The <see cref="TaskDefinition"/> of the registered task.</param>
/// <returns>A <see cref="Task"/> instance that represents the new task.</returns>
/// <example>
/// <code lang="cs"><![CDATA[
/// // Create a new task definition for the local machine and assign properties
/// TaskDefinition td = TaskService.Instance.NewTask();
/// td.RegistrationInfo.Description = "Does something";
///
/// // Add a trigger that, starting tomorrow, will fire every other week on Monday and Saturday
/// td.Triggers.Add(new WeeklyTrigger(DaysOfTheWeek.Monday | DaysOfTheWeek.Saturday, 2));
///
/// // Create an action that will launch Notepad whenever the trigger fires
/// td.Actions.Add("notepad.exe", "c:\\test.log");
///
/// // Register the task in the root folder of the local machine using the current user and the S4U logon type
/// TaskService.Instance.RootFolder.RegisterTaskDefinition("Test", td);
/// ]]></code></example>
public Task RegisterTaskDefinition(string path, [NotNull] TaskDefinition definition) => RegisterTaskDefinition(path, definition, TaskCreation.CreateOrUpdate,
definition.Principal.ToString(), null, definition.Principal.LogonType);
/// <summary>
/// Registers (creates) a task in a specified location using a <see cref="TaskDefinition" /> instance to define a 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="definition">The <see cref="TaskDefinition" /> of the registered 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. This will return <c>null</c> if <paramref name="createType"/> is set to <c>ValidateOnly</c> and there are no validation errors.
/// </returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Task names may not include any characters which are invalid for file names.
/// or
/// Task names ending with a period followed by three or fewer characters cannot be retrieved due to a bug in the native library.
/// </exception>
/// <exception cref="NotV1SupportedException">This LogonType is not supported on Task Scheduler 1.0.
/// or
/// Security settings are not available on Task Scheduler 1.0.
/// or
/// Registration triggers are not available on Task Scheduler 1.0.
/// or
/// XML validation not available on Task Scheduler 1.0.</exception>
/// <remarks>This method is effectively the "Save" method for tasks. It takes a modified <c>TaskDefinition</c> instance and registers it in the folder defined by this <c>TaskFolder</c> instance. Optionally, you can use this method to override the user, password and logon type defined in the definition and supply security against the task.</remarks>
/// <example>
/// <para>This first example registers a simple task with a single trigger and action using the default security.</para>
/// <code lang="cs"><![CDATA[
/// // Create a new task definition for the local machine and assign properties
/// TaskDefinition td = TaskService.Instance.NewTask();
/// td.RegistrationInfo.Description = "Does something";
///
/// // Add a trigger that, starting tomorrow, will fire every other week on Monday and Saturday
/// td.Triggers.Add(new WeeklyTrigger(DaysOfTheWeek.Monday | DaysOfTheWeek.Saturday, 2));
///
/// // Create an action that will launch Notepad whenever the trigger fires
/// td.Actions.Add("notepad.exe", "c:\\test.log");
///
/// // Register the task in the root folder of the local machine using the current user and the S4U logon type
/// TaskService.Instance.RootFolder.RegisterTaskDefinition("Test", td);
/// ]]></code>
/// <para>This example registers that same task using the SYSTEM account.</para>
/// <code lang="cs"><![CDATA[
/// TaskService.Instance.RootFolder.RegisterTaskDefinition("TaskName", taskDefinition, TaskCreation.CreateOrUpdate, "SYSTEM", null, TaskLogonType.ServiceAccount);
/// ]]></code>
/// <para>This example registers that same task using a specific username and password along with a security definition.</para>
/// <code lang="cs"><![CDATA[
/// TaskService.Instance.RootFolder.RegisterTaskDefinition("TaskName", taskDefinition, TaskCreation.CreateOrUpdate, "userDomain\\userName", "userPassword", TaskLogonType.Password, @"O:BAG:DUD:(A;ID;0x1f019f;;;BA)(A;ID;0x1f019f;;;SY)(A;ID;FA;;;BA)(A;;FR;;;BU)");
/// ]]></code></example>
public Task RegisterTaskDefinition([NotNull] string path, [NotNull] TaskDefinition definition, TaskCreation createType, string userId, string password = null, TaskLogonType logonType = TaskLogonType.S4U, string sddl = null)
{
if (definition.Actions.Count < 1 || definition.Actions.Count > 32)
throw new ArgumentOutOfRangeException(nameof(definition.Actions), @"A task must be registered with at least one action and no more than 32 actions.");
userId ??= definition.Principal.Account;
if (userId == string.Empty) userId = null;
User user = new User(userId);
if (v2Folder != null)
{
definition.Actions.ConvertUnsupportedActions();
if (logonType == TaskLogonType.ServiceAccount)
{
if (string.IsNullOrEmpty(userId) || !user.IsServiceAccount)
throw new ArgumentException(@"A valid system account name must be supplied for TaskLogonType.ServiceAccount. Valid entries are ""NT AUTHORITY\SYSTEM"", ""SYSTEM"", ""NT AUTHORITY\LOCALSERVICE"", or ""NT AUTHORITY\NETWORKSERVICE"".", nameof(userId));
if (password != null)
throw new ArgumentException(@"A password cannot be supplied when specifying TaskLogonType.ServiceAccount.", nameof(password));
}
/*else if ((LogonType == TaskLogonType.Password || LogonType == TaskLogonType.InteractiveTokenOrPassword ||
(LogonType == TaskLogonType.S4U && UserId != null && !user.IsCurrent)) && password == null)
{
throw new ArgumentException("A password must be supplied when specifying TaskLogonType.Password or TaskLogonType.InteractiveTokenOrPassword or TaskLogonType.S4U from another account.", nameof(password));
}*/
else if (logonType == TaskLogonType.Group && password != null)
{
throw new ArgumentException(@"A password cannot be supplied when specifying TaskLogonType.Group.", nameof(password));
}
// The following line compensates for an omission in the native library that never actually sets the registration date (thanks ixm7).
if (definition.RegistrationInfo.Date == DateTime.MinValue) definition.RegistrationInfo.Date = DateTime.Now;
var iRegTask = v2Folder.RegisterTaskDefinition(path, definition.v2Def, (int)createType, userId ?? user.Name, password, logonType, sddl);
if (createType == TaskCreation.ValidateOnly && iRegTask == null)
return null;
return Task.CreateTask(TaskService, iRegTask);
}
// Check for V1 invalid task names
string invChars = Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars()));
if (Regex.IsMatch(path, @"[" + invChars + @"]"))
throw new ArgumentOutOfRangeException(nameof(path), @"Task names may not include any characters which are invalid for file names.");
if (Regex.IsMatch(path, @"\.[^" + invChars + @"]{0,3}\z"))
throw new ArgumentOutOfRangeException(nameof(path), @"Task names ending with a period followed by three or fewer characters cannot be retrieved due to a bug in the native library.");
// Adds ability to set a password for a V1 task. Provided by Arcao.
TaskFlags flags = definition.v1Task.GetFlags();
if (logonType == TaskLogonType.InteractiveTokenOrPassword && string.IsNullOrEmpty(password))
logonType = TaskLogonType.InteractiveToken;
switch (logonType)
{
case TaskLogonType.Group:
case TaskLogonType.S4U:
case TaskLogonType.None:
throw new NotV1SupportedException("This LogonType is not supported on Task Scheduler 1.0.");
case TaskLogonType.InteractiveToken:
flags |= (TaskFlags.RunOnlyIfLoggedOn | TaskFlags.Interactive);
definition.v1Task.SetAccountInformation(user.Name, IntPtr.Zero);
break;
case TaskLogonType.ServiceAccount:
flags &= ~(TaskFlags.Interactive | TaskFlags.RunOnlyIfLoggedOn);
definition.v1Task.SetAccountInformation((String.IsNullOrEmpty(userId) || user.IsSystem) ? String.Empty : user.Name, IntPtr.Zero);
break;
case TaskLogonType.InteractiveTokenOrPassword:
flags |= TaskFlags.Interactive;
using (CoTaskMemString cpwd = new CoTaskMemString(password))
definition.v1Task.SetAccountInformation(user.Name, cpwd.DangerousGetHandle());
break;
case TaskLogonType.Password:
using (CoTaskMemString cpwd = new CoTaskMemString(password))
definition.v1Task.SetAccountInformation(user.Name, cpwd.DangerousGetHandle());
break;
default:
throw new ArgumentOutOfRangeException(nameof(logonType), logonType, null);
}
definition.v1Task.SetFlags(flags);
switch (createType)
{
case TaskCreation.Create:
case TaskCreation.CreateOrUpdate:
case TaskCreation.Disable:
case TaskCreation.Update:
if (createType == TaskCreation.Disable)
definition.Settings.Enabled = false;
definition.V1Save(path);
break;
case TaskCreation.DontAddPrincipalAce:
throw new NotV1SupportedException("Security settings are not available on Task Scheduler 1.0.");
case TaskCreation.IgnoreRegistrationTriggers:
throw new NotV1SupportedException("Registration triggers are not available on Task Scheduler 1.0.");
case TaskCreation.ValidateOnly:
throw new NotV1SupportedException("XML validation not available on Task Scheduler 1.0.");
default:
throw new ArgumentOutOfRangeException(nameof(createType), createType, null);
}
return new Task(TaskService, definition.v1Task);
}
/// <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>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString() => Path;
/// <summary>
/// Enumerates the tasks in the specified folder and its child folders.
/// </summary>
/// <param name="folder">The folder in which to start enumeration.</param>
/// <param name="filter">An optional filter to apply to the task list.</param>
/// <param name="recurse"><c>true</c> if subfolders are to be queried recursively.</param>
/// <returns>A <see cref="System.Collections.Generic.IEnumerator{Task}"/> that can be used to iterate through the tasks.</returns>
internal static IEnumerable<Task> EnumerateFolderTasks(TaskFolder folder, Predicate<Task> filter = null, bool recurse = true)
{
foreach (var task in folder.Tasks)
if (filter == null || filter(task))
yield return task;
if (!recurse) yield break;
foreach (var sfld in folder.SubFolders)
foreach (var task in EnumerateFolderTasks(sfld, filter))
yield return task;
}
}
}

View File

@ -0,0 +1,233 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Microsoft.Win32.TaskScheduler
{
/// <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 V2Interop.ITaskFolderCollection v2FolderList;
internal TaskFolderCollection() => v1FolderList = new TaskFolder[0];
internal TaskFolderCollection([NotNull] TaskFolder folder, [NotNull] V2Interop.ITaskFolderCollection iCollection)
{
parent = folder;
v2FolderList = iCollection;
}
/// <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="ICollection{T}"/> is read-only.</summary>
bool ICollection<TaskFolder>.IsReadOnly => false;
/// <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>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 (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="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 (var i = v2FolderList.Count; i > 0; i--)
if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase))
return true;
}
else
return item.Path == "\\";
return false;
}
/// <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)
{
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
if (array == null) throw new ArgumentNullException(nameof(array));
if (v2FolderList != null)
{
if (arrayIndex + Count > array.Length)
throw new ArgumentException();
foreach (var f in this)
array[arrayIndex++] = f;
}
else
{
if (arrayIndex + v1FolderList.Length > array.Length)
throw new ArgumentException();
v1FolderList.CopyTo(array, arrayIndex);
}
}
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
if (v1FolderList != null && v1FolderList.Length > 0)
{
v1FolderList[0].Dispose();
v1FolderList[0] = null;
}
if (v2FolderList != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(v2FolderList);
}
/// <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 System.Runtime.InteropServices.ComEnumerator<TaskFolder, V2Interop.ITaskFolder>(() => v2FolderList.Count, (object o) => v2FolderList[o], o => new TaskFolder(parent.TaskService, o));
return Array.AsReadOnly(v1FolderList).GetEnumerator();
}
/*
/// <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="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 (var i = v2FolderList.Count; i > 0; i--)
{
if (string.Equals(item.Path, v2FolderList[i].Path, StringComparison.CurrentCultureIgnoreCase))
{
try
{
parent.DeleteFolder(v2FolderList[i].Name);
OnNotifyPropertyChanged(nameof(Count));
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, i));
}
catch
{
return false;
}
return true;
}
}
}
return false;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
/// <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

@ -0,0 +1,53 @@
using System.Runtime.InteropServices;
namespace Microsoft.Win32.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.
/// </summary>
[ComImport, Guid("EAEC7A8F-27A0-4DDC-8675-14726A01A38A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity]
public interface ITaskHandlerStatus
{
/// <summary>
/// Tells the Task Scheduler about the percentage of completion of the COM handler.
/// </summary>
/// <param name="percentComplete">A value that indicates the percentage of completion for the COM handler.</param>
/// <param name="statusMessage">The message that is displayed in the Task Scheduler UI.</param>
void UpdateStatus([In] short percentComplete, [In, MarshalAs(UnmanagedType.BStr)] string statusMessage);
/// <summary>
/// Tells the Task Scheduler that the COM handler is completed.
/// </summary>
/// <param name="taskErrCode">The error code that the Task Scheduler will raise as an event.</param>
void TaskCompleted([In, MarshalAs(UnmanagedType.Error)] int taskErrCode);
}
}

View File

@ -0,0 +1,466 @@
using System;
using System.Runtime;
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;
using JetBrains.Annotations;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Specifies the access control rights that can be applied to Task Scheduler tasks.
/// </summary>
[Flags]
public enum TaskRights
{
/// <summary>Specifies the right to exert full control over a task folder or task, and to modify access control and audit rules. This value represents the right to do anything with a task and is the combination of all rights in this enumeration.</summary>
FullControl = 0x1f01ff,
/// <summary>Specifies the right to create tasks and folders, and to add or remove data from tasks. This right includes the following rights: .</summary>
Write = 0x120116,
/// <summary>Specifies the right to open and copy folders or tasks as read-only. This right includes the following rights: .</summary>
Read = 0x120089,
/// <summary>Specifies the right run tasks. This right includes the following rights: .</summary>
Execute = 0x120089,
/// <summary>The right to wait on a task.</summary>
Synchronize = 0x100000,
/// <summary>The right to change the owner of a task.</summary>
TakeOwnership = 0x80000,
/// <summary>Specifies the right to change the security and audit rules associated with a task or folder.</summary>
ChangePermissions = 0x40000,
/// <summary>The right to open and copy the access rules and audit rules for a task.</summary>
ReadPermissions = 0x20000,
/// <summary>The right to delete a folder or task.</summary>
Delete = 0x10000,
/// <summary>Specifies the right to open and write file system attributes to a folder or file. This does not include the ability to write data, extended attributes, or access and audit rules.</summary>
WriteAttributes = 0x100,
/// <summary>Specifies the right to open and copy file system attributes from a folder or task. For example, this value specifies the right to view the file creation or modified date. This does not include the right to read data, extended file system attributes, or access and audit rules.</summary>
ReadAttributes = 0x80,
/// <summary>Specifies the right to delete a folder and any tasks contained within that folder.</summary>
DeleteChild = 0x40,
/// <summary>Specifies the right to run a task.</summary>
ExecuteFile = 0x20,
/// <summary>Specifies the right to open and write extended file system attributes to a folder or file. This does not include the ability to write data, attributes, or access and audit rules.</summary>
WriteExtendedAttributes = 0x10,
/// <summary>Specifies the right to open and copy extended system attributes from a folder or task. For example, this value specifies the right to view author and content information. This does not include the right to read data, system attributes, or access and audit rules.</summary>
ReadExtendedAttributes = 8,
/// <summary>Specifies the right to append data to the end of a file.</summary>
AppendData = 4,
/// <summary>Specifies the right to open and write to a file or folder. This does not include the right to open and write file system attributes, extended file system attributes, or access and audit rules.</summary>
WriteData = 2,
/// <summary>Specifies the right to open and copy a task or folder. This does not include the right to read file system attributes, extended file system attributes, or access and audit rules.</summary>
ReadData = 1,
}
/// <summary>
/// Represents a set of access rights allowed or denied for a user or group. This class cannot be inherited.
/// </summary>
public sealed class TaskAccessRule : AccessRule
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskAccessRule"/> class, specifying 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 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 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] IdentityReference identity, TaskRights eventRights, AccessControlType type)
: this(identity, (int)eventRights, false, InheritanceFlags.None, PropagationFlags.None, type)
{
}
/// <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)
{
}
/// <summary>
/// Gets the rights allowed or denied by the access rule.
/// </summary>
/// <value>
/// A bitwise combination of <see cref="TaskRights"/> values indicating the rights allowed or denied by the access rule.
/// </value>
public TaskRights TaskRights => (TaskRights)AccessMask;
}
/// <summary>
/// Represents a set of access rights to be audited for a user or group. This class cannot be inherited.
/// </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)
{
}
/// <summary>
/// Gets the access rights affected by the audit rule.
/// </summary>
/// <value>
/// A bitwise combination of <see cref="TaskRights"/> values that indicates the rights affected by the audit rule.
/// </value>
/// <remarks><see cref="TaskAuditRule"/> objects are immutable. You can create a new audit rule representing a different user, different rights, or a different combination of AuditFlags values, but you cannot modify an existing audit rule.</remarks>
public TaskRights TaskRights => (TaskRights)AccessMask;
}
/// <summary>
/// Represents the Windows access control security for a Task Scheduler task. This class cannot be inherited.
/// </summary>
/// <remarks>
/// <para>A TaskSecurity object specifies access rights for a Task Scheduler task, and also specifies how access attempts are audited. Access rights to the task are expressed as rules, with each access rule represented by a <see cref="TaskAccessRule"/> object. Each auditing rule is represented by a <see cref="TaskAuditRule"/> object.</para>
/// <para>This mirrors the underlying Windows security system, in which each securable object has at most one discretionary access control list (DACL) that controls access to the secured object, and at most one system access control list (SACL) that specifies which access attempts are audited. The DACL and SACL are ordered lists of access control entries (ACE) that specify access and auditing for users and groups. A <see cref="TaskAccessRule"/> or <see cref="TaskAuditRule"/> object might represent more than one ACE.</para>
/// <para>Note</para>
/// <para>A <see cref="Task"/> object can represent a local task or a Task Scheduler task. Windows access control security is meaningful only for Task Scheduler tasks.</para>
/// <para>The TaskSecurity, <see cref="TaskAccessRule"/>, and <see cref="TaskAuditRule"/> classes hide the implementation details of ACLs and ACEs. They allow you to ignore the seventeen different ACE types and the complexity of correctly maintaining inheritance and propagation of access rights. These objects are also designed to prevent the following common access control errors:</para>
/// <list type="bullet">
/// <item><description>Creating a security descriptor with a null DACL. A null reference to a DACL allows any user to add access rules to an object, potentially creating a denial-of-service attack. A new TaskSecurity object always starts with an empty DACL, which denies all access for all users.</description></item>
/// <item><description>Violating the canonical ordering of ACEs. If the ACE list in the DACL is not kept in the canonical order, users might inadvertently be given access to the secured object. For example, denied access rights must always appear before allowed access rights. TaskSecurity objects maintain the correct order internally. </description></item>
/// <item><description>Manipulating security descriptor flags, which should be under resource manager control only.</description></item>
/// <item><description>Creating invalid combinations of ACE flags.</description></item>
/// <item><description>Manipulating inherited ACEs. Inheritance and propagation are handled by the resource manager, in response to changes you make to access and audit rules.</description></item>
/// <item><description>Inserting meaningless ACEs into ACLs.</description></item>
/// </list>
/// <para>The only capabilities not supported by the .NET security objects are dangerous activities that should be avoided by the majority of application developers, such as the following:</para>
/// <list type="bullet">
/// <item><description>Low-level tasks that are normally performed by the resource manager.</description></item>
/// <item><description>Adding or removing access control entries in ways that do not maintain the canonical ordering.</description></item>
/// </list>
/// <para>To modify Windows access control security for a task, use the <see cref="Task.GetAccessControl()"/> method to get the TaskSecurity object. Modify the security object by adding and removing rules, and then use the <see cref="Task.SetAccessControl"/> method to reattach it. </para>
/// <para>Important: Changes you make to a TaskSecurity object do not affect the access levels of the task until you call the <see cref="Task.SetAccessControl"/> method to assign the altered security object to the task.</para>
/// <para>To copy access control security from one task to another, use the <see cref="Task.GetAccessControl()"/> method to get a TaskSecurity object representing the access and audit rules for the first task, then use the <see cref="Task.SetAccessControl"/> method, or a constructor that accepts a TaskSecurity object, to assign those rules to the second task.</para>
/// <para>Users with an investment in the security descriptor definition language (SDDL) can use the <see cref="Task.SetSecurityDescriptorSddlForm"/> method to set access rules for a task, and the <see cref="Task.GetSecurityDescriptorSddlForm"/> method to obtain a string that represents the access rules in SDDL format. This is not recommended for new development.</para>
/// </remarks>
public sealed class TaskSecurity : CommonObjectSecurity
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskSecurity"/> class with default values.
/// </summary>
public TaskSecurity()
: base(false)
{
}
/// <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="task">The task.</param>
/// <param name="sections">The sections of the ACL to retrieve.</param>
public TaskSecurity([NotNull] Task task, AccessControlSections sections = Task.defaultAccessControlSections)
: base(false)
{
SetSecurityDescriptorSddlForm(task.GetSecurityDescriptorSddlForm(Convert(sections)), sections);
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.
/// </summary>
/// <returns>A <see cref="Type"/> object representing the <see cref="TaskRights"/> enumeration.</returns>
public override Type AccessRightType => typeof(TaskRights);
/// <summary>
/// Gets the type that the TaskSecurity class uses to represent access rules.
/// </summary>
/// <returns>A <see cref="Type"/> object representing the <see cref="TaskAccessRule"/> class.</returns>
public override Type AccessRuleType => typeof(TaskAccessRule);
/// <summary>
/// Gets the type that the TaskSecurity class uses to represent audit rules.
/// </summary>
/// <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>
/// <param name="identityReference">An <see cref="IdentityReference"/> that identifies the user or group the rule applies to.</param>
/// <param name="accessMask">A bitwise combination of <see cref="TaskRights"/> values specifying the access rights to allow or deny, cast to an integer.</param>
/// <param name="isInherited">Meaningless for tasks, because they have no hierarchy.</param>
/// <param name="inheritanceFlags">Meaningless for tasks, because they have no hierarchy.</param>
/// <param name="propagationFlags">Meaningless for tasks, because they have no hierarchy.</param>
/// <param name="type">One of the <see cref="AccessControlType"/> values specifying whether the rights are allowed or denied.</param>
/// <returns>
/// The <see cref="T:System.Security.AccessControl.AccessRule" /> object that this method creates.
/// </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>
/// <param name="identityReference">An <see cref="IdentityReference"/> that identifies the user or group the rule applies to.</param>
/// <param name="accessMask">A bitwise combination of <see cref="TaskRights"/> values specifying the access rights to audit, cast to an integer.</param>
/// <param name="isInherited">Meaningless for tasks, because they have no hierarchy.</param>
/// <param name="inheritanceFlags">Meaningless for tasks, because they have no hierarchy.</param>
/// <param name="propagationFlags">Meaningless for tasks, because they have no hierarchy.</param>
/// <param name="flags">One of the <see cref="AuditFlags"/> values specifying whether to audit successful access, failed access, or both.</param>
/// <returns>
/// A <see cref="TaskAuditRule"/> object representing the specified audit rule for the specified user. The return type of the method is the base class, <see cref="AuditRule"/>, but the return value can be cast safely to the derived class.
/// </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>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString() => GetSecurityDescriptorSddlForm(Task.defaultAccessControlSections);
private static SecurityInfos Convert(AccessControlSections si)
{
SecurityInfos ret = 0;
if ((si & AccessControlSections.Audit) != 0)
ret |= SecurityInfos.SystemAcl;
if ((si & AccessControlSections.Access) != 0)
ret |= SecurityInfos.DiscretionaryAcl;
if ((si & AccessControlSections.Group) != 0)
ret |= SecurityInfos.Group;
if ((si & AccessControlSections.Owner) != 0)
ret |= SecurityInfos.Owner;
return ret;
}
private static AccessControlSections Convert(SecurityInfos si)
{
AccessControlSections ret = AccessControlSections.None;
if ((si & SecurityInfos.SystemAcl) != 0)
ret |= AccessControlSections.Audit;
if ((si & SecurityInfos.DiscretionaryAcl) != 0)
ret |= AccessControlSections.Access;
if ((si & SecurityInfos.Group) != 0)
ret |= AccessControlSections.Group;
if ((si & SecurityInfos.Owner) != 0)
ret |= AccessControlSections.Owner;
return ret;
}
private AccessControlSections GetAccessControlSectionsFromChanges()
{
AccessControlSections none = AccessControlSections.None;
if (AccessRulesModified)
{
none = AccessControlSections.Access;
}
if (AuditRulesModified)
{
none |= AccessControlSections.Audit;
}
if (OwnerModified)
{
none |= AccessControlSections.Owner;
}
if (GroupModified)
{
none |= AccessControlSections.Group;
}
return none;
}
/// <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="task">The task 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] Task task, AccessControlSections includeSections = Task.defaultAccessControlSections)
{
WriteLock();
try
{
AccessControlSections accessControlSectionsFromChanges = GetAccessControlSectionsFromChanges();
if (accessControlSectionsFromChanges != AccessControlSections.None)
{
task.SetSecurityDescriptorSddlForm(GetSecurityDescriptorSddlForm(accessControlSectionsFromChanges));
OwnerModified = GroupModified = AccessRulesModified = AuditRulesModified = false;
}
}
finally
{
WriteUnlock();
}
}
/// <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);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net20;net35;net40;net452;netstandard2.0;netcoreapp2.0;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1;net5.0-windows</TargetFrameworks>
<!--<TargetFrameworks>netcoreapp3.1</TargetFrameworks>-->
<AssemblyName>Microsoft.Win32.TaskScheduler</AssemblyName>
<RootNamespace>Microsoft.Win32.TaskScheduler</RootNamespace>
<PackageId>TaskScheduler</PackageId>
<Title>Task Scheduler Managed Wrapper (the original Microsoft.Win32.TaskScheduler)</Title>
<Description>Provides a single assembly wrapper for the 1.0 and 2.0 versions of Task Scheduler found in all Microsoft operating systems post Windows 98. It simplifies the coding, aggregates the multiple versions and allows for localization support.</Description>
<PackageTags>task;interop;windows;taskscheduler;scheduler;.net;task scheduler</PackageTags>
</PropertyGroup>
<ItemGroup>
<None Remove="TaskService.bmp" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TaskService.bmp" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net2')) Or $(TargetFramework.StartsWith('net3')) Or $(TargetFramework.StartsWith('net4')) ">
<Reference Include="System.Drawing" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net45')) ">
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.IO.Compression" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Reflection" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
<PackageReference Include="System.Security.Permissions" Version="5.0.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' Or $(TargetFramework.StartsWith('netcoreapp')) Or $(TargetFramework.StartsWith('net5')) ">
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="System.Diagnostics.EventLog" Version="5.0.0" />
<PackageReference Include="System.Security.AccessControl" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,443 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Microsoft.Win32.TaskScheduler
{
public abstract partial class Trigger
{
/// <summary>Creates a trigger using a cron string.</summary>
/// <param name="cronString">String using cron defined syntax for specifying a time interval. See remarks for syntax.</param>
/// <returns>Array of <see cref="Trigger"/> representing the specified cron string.</returns>
/// <exception cref="System.NotImplementedException">Unsupported cron string.</exception>
/// <remarks>
/// <note type="note"> This method does not support all combinations of cron strings. Please test extensively before use. Please post an issue with any
/// syntax that should work, but doesn't.</note>
/// <para>The following combinations are known <c>not</c> to work:</para>
/// <list type="bullet">
/// <item><description>Intervals on months (e.g. "* * * */5 *")</description></item>
/// <item><description>Intervals on DOW (e.g. "* * * * MON/3")</description></item>
/// </list>
/// <para>
/// This section borrows liberally from the site http://www.nncron.ru/help/EN/working/cron-format.htm. The cron format consists of five fields separated
/// by white spaces:
/// </para>
/// <code>
/// &lt;Minute&gt; &lt;Hour&gt; &lt;Day_of_the_Month&gt; &lt;Month_of_the_Year&gt; &lt;Day_of_the_Week&gt;
/// </code>
/// <para>Each item has bounds as defined by the following:</para>
/// <code>
/// * * * * *
/// | | | | |
/// | | | | +---- Day of the Week (range: 1-7, 1 standing for Monday)
/// | | | +------ Month of the Year (range: 1-12)
/// | | +-------- Day of the Month (range: 1-31)
/// | +---------- Hour (range: 0-23)
/// +------------ Minute (range: 0-59)
/// </code>
/// <para>Any of these 5 fields may be an asterisk (*). This would mean the entire range of possible values, i.e. each minute, each hour, etc.</para>
/// <para>
/// Any of the first 4 fields can be a question mark ("?"). It stands for the current time, i.e. when a field is processed, the current time will be
/// substituted for the question mark: minutes for Minute field, hour for Hour field, day of the month for Day of month field and month for Month field.
/// </para>
/// <para>Any field may contain a list of values separated by commas, (e.g. 1,3,7) or a range of values (two integers separated by a hyphen, e.g. 1-5).</para>
/// <para>
/// After an asterisk (*) or a range of values, you can use character / to specify that values are repeated over and over with a certain interval between
/// them. For example, you can write "0-23/2" in Hour field to specify that some action should be performed every two hours (it will have the same effect
/// as "0,2,4,6,8,10,12,14,16,18,20,22"); value "*/4" in Minute field means that the action should be performed every 4 minutes, "1-30/3" means the same
/// as "1,4,7,10,13,16,19,22,25,28".
/// </para>
/// </remarks>
public static Trigger[] FromCronFormat([NotNull] string cronString)
{
var cron = CronExpression.Parse(cronString);
System.Diagnostics.Debug.WriteLine($"{cronString}=M:{cron.Minutes}; H:{cron.Hours}; D:{cron.Days}; M:{cron.Months}; W:{cron.DOW}");
var ret = new List<Trigger>();
// There isn't a clean mechanism to handle intervals on DOW or months, so punt
//if (cron.DOW.IsIncr) throw new NotSupportedException();
//if (cron.Months.IsIncr) throw new NotSupportedException();
// WeeklyTrigger
if (cron.Days.FullRange && cron.Months.FullRange && !cron.DOW.IsEvery)
{
var tr = new WeeklyTrigger(cron.DOW.ToDOW());
ret.AddRange(ProcessCronTimes(cron, tr));
}
// MonthlyDOWTrigger
if (!cron.DOW.FullRange && (!cron.Days.FullRange || !cron.Months.FullRange))
{
var tr = new MonthlyDOWTrigger(cron.DOW.ToDOW(), cron.Months.ToMOY(), WhichWeek.AllWeeks);
ret.AddRange(ProcessCronTimes(cron, tr));
}
// MonthlyTrigger
if (!cron.Days.FullRange || !cron.Months.FullRange && cron.DOW.FullRange)
{
var tr = new MonthlyTrigger(1, cron.Months.ToMOY()) { DaysOfMonth = cron.Days.Values.ToArray() };
ret.AddRange(ProcessCronTimes(cron, tr));
}
// DailyTrigger
if (cron.Days.FullRange && cron.Months.FullRange && cron.DOW.IsEvery)
{
var tr = new DailyTrigger((short)cron.Days.Increment);
ret.AddRange(ProcessCronTimes(cron, tr));
}
// Fail out
if (ret.Count == 0)
throw new NotSupportedException();
return ret.ToArray();
}
private static IEnumerable<Trigger> ProcessCronTimes(CronExpression cron, Trigger baseTrigger)
{
// Sequential hours, every minute
// "* * * * *"
// "* 2-6 * * *"
if (cron.Minutes.FullRange && (cron.Hours.IsEvery || cron.Hours.IsRange))
{
System.Diagnostics.Debug.WriteLine("Minutes.FullRange && (Hours.IsEvery || Hours.IsRange)");
yield return MakeTrigger(
new TimeSpan(cron.Hours.FirstValue, 0, 0),
TimeSpan.FromMinutes(cron.Minutes.Increment),
TimeSpan.FromHours(cron.Hours.Duration));
}
// Non-sequential hours, every minute
// "* 3,5,6 * * *"
// "* 3-15/3 * * *"
else if (cron.Minutes.FullRange && (cron.Hours.IsList || cron.Hours.IsIncr))
{
System.Diagnostics.Debug.WriteLine("Minutes.FullRange && (Hours.IsList || Hours.IsIncr)");
foreach (var h in cron.Hours.Values)
{
yield return MakeTrigger(
new TimeSpan(h, 0, 0),
TimeSpan.FromMinutes(cron.Minutes.Increment),
TimeSpan.FromHours(1));
}
}
// Non-repeating minutes, every hour
// "3,6 * * * *" Every hour starting at 12:03 and 12:06
// "3-33 * * * *"
// "3-33/6 * * * *"
// "3,6 * 3-5 * *"
// "3-33 3-5 * * *"
// "3-33/6 3-5 * * *"
else if (!cron.Minutes.FullRange && (cron.Hours.IsEvery || cron.Hours.IsRange))
{
System.Diagnostics.Debug.WriteLine("!Minutes.FullRange && (Hours.IsEvery || Hours.IsRange)");
foreach (var m in cron.Minutes.Values)
{
yield return MakeTrigger(
new TimeSpan(cron.Hours.FirstValue, m, 0),
TimeSpan.FromHours(1),
TimeSpan.FromHours(cron.Hours.Duration));
}
}
// Sequential or repeating minutes, and non-sequential hours
else if ((cron.Minutes.IsRange || cron.Minutes.IsIncr) && (cron.Hours.IsList || cron.Hours.IsIncr))
{
System.Diagnostics.Debug.WriteLine("(Minutes.IsRange || Minutes.IsIncr) && (Hours.IsList || Hours.IsIncr)");
foreach (var h in cron.Hours.Values)
{
yield return MakeTrigger(
new TimeSpan(h, cron.Minutes.FirstValue, 0),
TimeSpan.FromMinutes(cron.Minutes.Increment),
TimeSpan.FromMinutes(cron.Minutes.Duration));
}
}
// Non-sequential, hours and minutes
// "3,6 3,6 * * *" Every day at 3:03, 3:06, 6:03 and 6:06
// "3/6 3/6 * * *" Every day at 3:03, 3:06, 6:03 and 6:06
else
{
System.Diagnostics.Debug.WriteLine("Minutes.IsList && (Hours.IsIncr || Hours.IsList)");
foreach (var h in cron.Hours.Values)
foreach (var m in cron.Minutes.Values)
yield return MakeTrigger(new TimeSpan(h, m, 0));
}
Trigger MakeTrigger(TimeSpan start, TimeSpan interval = default, TimeSpan duration = default)
{
var newTr = (Trigger)baseTrigger.Clone();
newTr.StartBoundary = newTr.StartBoundary.Date + start;
if (interval != default)
{
newTr.Repetition.Interval = interval;
newTr.Repetition.Duration = duration;
}
return newTr;
}
}
internal class CronExpression
{
private FieldVal[] Fields = new FieldVal[5];
private CronExpression() { }
public enum CronFieldType { Minutes, Hours, Days, Months, DaysOfWeek };
public FieldVal Days => Fields[2];
public FieldVal DOW => Fields[4];
public FieldVal Hours => Fields[1];
public FieldVal Minutes => Fields[0];
public FieldVal Months => Fields[3];
public static CronExpression Parse(string cronString)
{
var ret = new CronExpression();
if (cronString == null)
throw new ArgumentNullException(nameof(cronString));
var tokens = cronString.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length != 5)
{
throw new ArgumentException($"'{cronString}' is not a valid crontab expression. It must contain at least 5 components of a schedule "
+ "(in the sequence of minutes, hours, days, months, days of week).");
}
// min, hr, days, months, daysOfWeek
for (var i = 0; i < ret.Fields.Length; i++)
ret.Fields[i] = FieldVal.Parse(tokens[i], (CronFieldType)i);
return ret;
}
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;
public FieldVal(CronFieldType cft) { this.cft = cft; flags = 0; vals = new int[0]; incr = 1; FullRange = false; }
enum FieldFlags { List, Every, Range, Increment };
public int Duration => vals.Length == 1 ? 1 : vals[1] - vals[0] + 1;
public int Increment => incr;
public bool IsEvery { get => flags == FieldFlags.Every; private set => flags = FieldFlags.Every; }
public bool IsIncr { get => flags == FieldFlags.Increment; private set => flags = FieldFlags.Increment; }
public bool IsList { get => flags == 0; private set => flags = FieldFlags.List; }
public bool IsRange { get => flags == FieldFlags.Range; private set => flags = FieldFlags.Range; }
public bool FullRange { get; private set; }
public int FirstValue => vals[0];
public IEnumerable<int> Values
{
get
{
if (flags == 0)
{
foreach (var i in vals)
yield return i;
}
else
{
for (int i = vals[0]; i <= vals[1]; i += incr)
yield return i;
}
}
}
public DaysOfTheWeek ToDOW()
{
if (IsEvery) return DaysOfTheWeek.AllDays;
DaysOfTheWeek ret = 0;
foreach (var i in Values)
ret |= (DaysOfTheWeek)(1 << i);
return ret;
}
public MonthsOfTheYear ToMOY()
{
if (IsEvery) return MonthsOfTheYear.AllMonths;
MonthsOfTheYear ret = 0;
foreach (var i in Values)
ret |= (MonthsOfTheYear)(1 << (i - 1));
return ret;
}
public static FieldVal Parse(string str, CronFieldType cft)
{
var res = new FieldVal(cft);
if (string.IsNullOrEmpty(str))
throw new ArgumentNullException(nameof(str), "A crontab field value cannot be empty.");
// Do substitutions
str = DoSubs(str, cft);
// Look first for a list of values (e.g. 1,2,3).
if (System.Text.RegularExpressions.Regex.IsMatch(str, @"^\d+(,\d+)*$"))
{
if (str.Contains("/")) throw new NotSupportedException();
res.vals = str.Split(',').Select(ParseInt).OrderBy(i => i).Distinct().ToArray();
res.Validate();
return res;
}
// Look for *|nn[-nn][/n] pattern
var match = System.Text.RegularExpressions.Regex.Match(str, rangeRegEx);
if (match.Success)
{
bool hasAst = res.FullRange = match.Groups["A"].Success;
if (match.Groups["I"].Success)
{
res.incr = ParseInt(match.Groups["I"].Value);
res.IsIncr = true;
}
else
{
if (hasAst)
res.IsEvery = true;
else
res.IsRange = true;
}
var mm = validRange[cft];
var start = hasAst ? mm.Min : ParseInt(match.Groups["D1"].Value);
var end = hasAst ? mm.Max : (match.Groups["D2"].Success ? ParseInt(match.Groups["D2"].Value) : (res.IsIncr ? mm.Max : start));
if (end < start) throw new ArgumentOutOfRangeException();
if (start == end && res.IsRange)
{
res.IsList = true;
res.vals = new[] { start };
}
else
res.vals = new[] { start, end };
res.Validate();
return res;
}
throw new FormatException();
}
public override string ToString() => $"Type:{flags}; Vals:{string.Join(",", vals.Select(i => i.ToString()).ToArray())}; Incr:{incr}";
private void Validate()
{
var l = validRange[cft];
if (vals.Any(i => i < l.Min || i > l.Max)) throw new ArgumentOutOfRangeException();
if (IsIncr && (incr < l.Min || incr > l.Max)) throw new ArgumentOutOfRangeException();
}
private static string DoSubs(string str, CronFieldType cft)
{
var sb = new System.Text.StringBuilder(str);
// Handle SUN-SAT strings
if (cft == CronFieldType.DaysOfWeek)
{
foreach (var kv in dow)
sb.Replace(kv.Key, kv.Value);
}
// Handle JANDEC strings
if (cft == CronFieldType.Months)
{
foreach (var kv in mon)
sb.Replace(kv.Key, kv.Value);
}
// Check for "?" and substitute current time
if (sb.Length == 1 && sb.ToString() == "?")
{
var now = DateTime.Now;
var nval = 0;
switch (cft)
{
case CronFieldType.Minutes:
nval = now.Minute;
break;
case CronFieldType.Hours:
nval = now.Hour;
break;
case CronFieldType.Days:
nval = now.Day;
break;
case CronFieldType.Months:
nval = now.Month;
break;
case CronFieldType.DaysOfWeek:
nval = (int)now.DayOfWeek;
break;
default:
break;
}
sb.Remove(0, 1);
sb.Append(nval);
}
// Expand or collapse ranges
var minMax = validRange[cft];
foreach (System.Text.RegularExpressions.Match m in System.Text.RegularExpressions.Regex.Matches(sb.ToString(), @"(\d+)-(\d+)"))
{
var low = ParseInt(m.Groups[1].Value);
var high = ParseInt(m.Groups[2].Value);
if (low == minMax.Min && high == minMax.Max)
sb.Replace(m.Value, "*");
else if (sb.ToString().Contains(','))
{
var rsb = new System.Text.StringBuilder(low.ToString());
for (int i = low; i < high; i++)
rsb.Append($",{i + 1}");
sb.Replace(m.Value, rsb.ToString());
}
}
return sb.ToString();
}
private static int ParseInt(string str) => int.Parse(str.Trim());
private struct MinMax
{
public int Min, Max;
public MinMax(int min, int max) { Min = min; Max = max; }
}
}
}
}
}

View File

@ -0,0 +1,494 @@
using JetBrains.Annotations;
using System;
namespace Microsoft.Win32.TaskScheduler
{
public sealed partial class TaskService
{
/// <summary>Initial call for a Fluent model of creating a task.</summary>
/// <param name="path">The path of the program to run.</param>
/// <returns>An <see cref="Fluent.ActionBuilder"/> instance.</returns>
public Fluent.ActionBuilder Execute([NotNull] string path) => new Fluent.ActionBuilder(new Fluent.BuilderInfo(this), path);
}
namespace Fluent
{
/// <summary>Fluent helper class. Not intended for use.</summary>
public class ActionBuilder : BaseBuilder
{
internal ActionBuilder(BuilderInfo taskBuilder, string path)
: base(taskBuilder) => TaskDef.Actions.Add(new ExecAction(path));
/// <summary>Adds a trigger that executes at logon of all users.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder AtLogon() => new TriggerBuilder(tb, TaskTriggerType.Logon);
/// <summary>Adds a trigger that executes at logon of a specific user.</summary>
/// <param name="userId">The user id.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder AtLogonOf(string userId)
{
var b = new TriggerBuilder(tb, TaskTriggerType.Logon);
((LogonTrigger)b.trigger).UserId = userId;
return b;
}
/// <summary>Adds a trigger that executes at task registration.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder AtTaskRegistration() => new TriggerBuilder(tb, TaskTriggerType.Registration);
/// <summary>Adds a trigger that executes every day or week.</summary>
/// <param name="num">The interval of days or weeks.</param>
/// <returns><see cref="IntervalTriggerBuilder"/> instance.</returns>
public IntervalTriggerBuilder Every(short num) => new IntervalTriggerBuilder(tb, num);
/// <summary>Adds a trigger that executes monthly on specific days.</summary>
/// <param name="moy">The months of the year in which to run.</param>
/// <returns><see cref="MonthlyTriggerBuilder"/> instance.</returns>
public MonthlyTriggerBuilder InTheMonthOf(MonthsOfTheYear moy) => new MonthlyTriggerBuilder(tb, moy);
/// <summary>Adds a working directory to the <see cref="ExecAction"/>.</summary>
/// <param name="dir">The directory.</param>
/// <returns><see cref="ActionBuilder"/> instance.</returns>
public ActionBuilder InWorkingDirectory([NotNull] string dir)
{
((ExecAction)TaskDef.Actions[0]).WorkingDirectory = dir;
return this;
}
/// <summary>Adds a trigger that executes monthly on certain days of the week.</summary>
/// <param name="dow">The days of the week on which to run.</param>
/// <returns><see cref="MonthlyDOWTriggerBuilder"/> instance.</returns>
public MonthlyDOWTriggerBuilder OnAll(DaysOfTheWeek dow) => new MonthlyDOWTriggerBuilder(tb, dow);
/// <summary>Adds a trigger that executes at system startup.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder OnBoot() => new TriggerBuilder(tb, TaskTriggerType.Boot);
/// <summary>Adds a trigger that executes once at a specific time.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Once() => new TriggerBuilder(tb, TaskTriggerType.Time);
/// <summary>Adds a trigger that executes when system is idle.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder OnIdle() => new TriggerBuilder(tb, TaskTriggerType.Idle);
/// <summary>Adds a trigger that executes once at specified state change.</summary>
/// <param name="changeType">Type of the change.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder OnStateChange(TaskSessionStateChangeType changeType)
{
var b = new TriggerBuilder(tb, TaskTriggerType.SessionStateChange);
((SessionStateChangeTrigger)b.trigger).StateChange = changeType;
return b;
}
/// <summary>Adds arguments to the <see cref="ExecAction"/>.</summary>
/// <param name="args">The arguments.</param>
/// <returns><see cref="ActionBuilder"/> instance.</returns>
public ActionBuilder WithArguments([NotNull] string args)
{
((ExecAction)TaskDef.Actions[0]).Arguments = args;
return this;
}
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public abstract class BaseBuilder
{
internal BuilderInfo tb;
internal BaseBuilder(BuilderInfo taskBuilder) => tb = taskBuilder;
/// <summary>Transitions to settings syntax.</summary>
public SettingsBuilder When => new SettingsBuilder(tb);
internal TaskDefinition TaskDef => tb.td;
/// <summary>Assigns the name of the task and registers it.</summary>
/// <param name="name">The name.</param>
/// <returns>A registered <see cref="Task"/> instance.</returns>
public Task AsTask([NotNull] string name) => tb.ts.RootFolder.RegisterTaskDefinition(name, TaskDef);
/// <summary>Assigns the name of the task and registers it.</summary>
/// <param name="name">The name.</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>
/// <returns>A registered <see cref="Task"/> instance.</returns>
public Task AsTask([NotNull] string name, TaskCreation createType, string userId, string password = null, TaskLogonType logonType = TaskLogonType.S4U) => tb.ts.RootFolder.RegisterTaskDefinition(name, TaskDef, createType, userId, password, logonType);
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public class IntervalTriggerBuilder : BaseBuilder
{
internal short interval = 0;
internal IntervalTriggerBuilder(BuilderInfo taskBuilder, short interval)
: base(taskBuilder) => this.interval = interval;
/// <summary>Specifies that an Every target uses days as the interval.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Days() => new TriggerBuilder(tb) { trigger = TaskDef.Triggers.Add(new DailyTrigger(interval)) };
/// <summary>Specifies that an Every target uses weeks as the interval.</summary>
/// <returns><see cref="WeeklyTriggerBuilder"/> instance.</returns>
public WeeklyTriggerBuilder Weeks() => new WeeklyTriggerBuilder(tb, interval);
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public class MonthlyDOWTriggerBuilder : BaseBuilder
{
private TriggerBuilder trb;
internal MonthlyDOWTriggerBuilder(BuilderInfo taskBuilder, DaysOfTheWeek dow)
: base(taskBuilder) => trb = new TriggerBuilder(taskBuilder, dow);
/// <summary>Updates a monthly trigger to specify in which weeks of the month it will run.</summary>
/// <param name="ww">The week.</param>
/// <returns><see cref="MonthlyDOWTriggerBuilder"/> instance.</returns>
public MonthlyDOWTriggerBuilder In(WhichWeek ww)
{
((MonthlyDOWTrigger)trb.trigger).WeeksOfMonth = ww;
return this;
}
/// <summary>Updates a monthly trigger to specify the months of the year in which it will run.</summary>
/// <param name="moy">The month of the year.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Of(MonthsOfTheYear moy)
{
((MonthlyDOWTrigger)trb.trigger).MonthsOfYear = moy;
return trb;
}
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public class MonthlyTriggerBuilder : BaseBuilder
{
private TriggerBuilder trb;
internal MonthlyTriggerBuilder(BuilderInfo taskBuilder, MonthsOfTheYear moy)
: base(taskBuilder) => trb = new TriggerBuilder(taskBuilder, moy);
/// <summary>Updates a monthly trigger to specify the days of the month on which it will run.</summary>
/// <param name="days">The days.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder OnTheDays([NotNull] params int[] days)
{
((MonthlyTrigger)trb.trigger).DaysOfMonth = days;
return trb;
}
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public class SettingsBuilder : BaseBuilder
{
internal SettingsBuilder(BuilderInfo taskBuilder) : base(taskBuilder)
{
}
/// <summary>Indicates that the task will be started even if the computer is running on battery power.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder AllowingStartIfOnBatteries()
{
TaskDef.Settings.DisallowStartIfOnBatteries = false;
return this;
}
/// <summary>
/// Indicates that the task will be started even if the task is triggered to run in a Remote Applications Integrated Locally
/// (RAIL) session.
/// </summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder AllowingStartOnRemoteAppSession()
{
TaskDef.Settings.DisallowStartOnRemoteAppSession = false;
return this;
}
/// <summary>Sets the task data to a string.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder DataIs(string data)
{
TaskDef.Data = data; return this;
}
/// <summary>Sets the amount of time that the Task Scheduler will wait before deleting the task after it expires.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder DeletingTaskAfter(TimeSpan duration)
{
TaskDef.Settings.DeleteExpiredTaskAfter = duration;
return this;
}
/// <summary>Indicates that the task cannot be started with the Run command or the Context menu.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder DisallowingDemandStart()
{
TaskDef.Settings.AllowDemandStart = false;
return this;
}
/// <summary>Indicates that the task may not be terminated by using TerminateProcess.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder DisallowingHardTerminate()
{
TaskDef.Settings.AllowHardTerminate = false;
return this;
}
/// <summary>Sets the amount of time that is allowed to complete the task.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder ExecutingAtMost(TimeSpan duration)
{
TaskDef.Settings.ExecutionTimeLimit = duration;
return this;
}
/// <summary>Sets the policy that defines how the Task Scheduler handles multiple instances of the task.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder InstancesAre(TaskInstancesPolicy policy)
{
TaskDef.Settings.MultipleInstances = policy;
return this;
}
/// <summary>Indicates that the task will not be stopped if the computer switches to battery power.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder NotStoppingIfGoingOnBatteries()
{
TaskDef.Settings.StopIfGoingOnBatteries = true;
return this;
}
/// <summary>Indicates that the Task Scheduler will run the task only if the computer is in an idle condition.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder OnlyIfIdle()
{
TaskDef.Settings.RunOnlyIfIdle = true;
return this;
}
/// <summary>Indicates that the Task Scheduler will run the task only when a network is available.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder OnlyIfNetworkAvailable()
{
TaskDef.Settings.RunOnlyIfNetworkAvailable = true;
return this;
}
/// <summary>Sets the priority level of the task.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder PriorityIs(System.Diagnostics.ProcessPriorityClass priority)
{
TaskDef.Settings.Priority = priority;
return this;
}
/// <summary>Sets a value that specifies how long the Task Scheduler will attempt to restart the task.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder RestartingEvery(TimeSpan interval)
{
TaskDef.Settings.RestartInterval = interval;
return this;
}
/// <summary>Indicates that the Task Scheduler can start the task at any time after its scheduled time has passed.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder StartingWhenAvailable()
{
TaskDef.Settings.StartWhenAvailable = true;
return this;
}
/// <summary>Indicates that the Task Scheduler will wake the computer when it is time to run the task.</summary>
/// <returns><see cref="SettingsBuilder"/> instance.</returns>
public SettingsBuilder WakingToRun()
{
TaskDef.Settings.WakeToRun = true;
return this;
}
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public class TriggerBuilder : BaseBuilder
{
internal Trigger trigger;
internal TriggerBuilder(BuilderInfo taskBuilder)
: base(taskBuilder)
{
}
internal TriggerBuilder(BuilderInfo taskBuilder, DaysOfTheWeek dow)
: this(taskBuilder) => TaskDef.Triggers.Add(trigger = new MonthlyDOWTrigger(dow));
internal TriggerBuilder(BuilderInfo taskBuilder, MonthsOfTheYear moy)
: this(taskBuilder) => TaskDef.Triggers.Add(trigger = new MonthlyTrigger() { MonthsOfYear = moy });
internal TriggerBuilder(BuilderInfo taskBuilder, TaskTriggerType taskTriggerType)
: this(taskBuilder) => TaskDef.Triggers.Add(trigger = Trigger.CreateTrigger(taskTriggerType));
/// <summary>Specifies a date on which a trigger will no longer run.</summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Ending(int year, int month, int day)
{
trigger.EndBoundary = new DateTime(year, month, day, trigger.StartBoundary.Hour, trigger.StartBoundary.Minute, trigger.StartBoundary.Second);
return this;
}
/// <summary>Specifies a date and time on which a trigger will no longer run.</summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
/// <param name="hour">The hour.</param>
/// <param name="min">The min.</param>
/// <param name="sec">The sec.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Ending(int year, int month, int day, int hour, int min, int sec)
{
trigger.EndBoundary = new DateTime(year, month, day, hour, min, sec);
return this;
}
/// <summary>Specifies a date and time on which a trigger will no longer run.</summary>
/// <param name="dt">A string representing a DateTime and parsable via <see cref="DateTime.Parse(string)"/>.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Ending([NotNull] string dt)
{
trigger.EndBoundary = DateTime.Parse(dt);
return this;
}
/// <summary>Specifies a date and time on which a trigger will no longer run.</summary>
/// <param name="dt">The DateTime value.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Ending(DateTime dt)
{
trigger.EndBoundary = dt;
return this;
}
/// <summary>Determines whether this trigger is disabled.</summary>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder IsDisabled()
{
trigger.Enabled = false;
return this;
}
/// <summary>Specifies a repetition interval for the trigger.</summary>
/// <param name="span">The interval span.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder RepeatingEvery(TimeSpan span)
{
trigger.Repetition.Interval = span;
return this;
}
/// <summary>Specifies a repetition interval for the trigger.</summary>
/// <param name="span">The interval span string. Must be parsable by <see cref="TimeSpan.Parse(string)"/>.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder RepeatingEvery([NotNull] string span)
{
trigger.Repetition.Interval = TimeSpan.Parse(span);
return this;
}
/// <summary>Specifies the maximum amount of time to repeat the execution of a trigger.</summary>
/// <param name="span">The duration span.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder RunningAtMostFor(TimeSpan span)
{
trigger.Repetition.Duration = span;
return this;
}
/// <summary>Specifies the maximum amount of time to repeat the execution of a trigger.</summary>
/// <param name="span">The duration span string. Must be parsable by <see cref="TimeSpan.Parse(string)"/>.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder RunningAtMostFor([NotNull] string span)
{
trigger.Repetition.Duration = TimeSpan.Parse(span);
return this;
}
/// <summary>Specifies a date on which a trigger will start.</summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Starting(int year, int month, int day)
{
trigger.StartBoundary = new DateTime(year, month, day, trigger.StartBoundary.Hour, trigger.StartBoundary.Minute, trigger.StartBoundary.Second);
return this;
}
/// <summary>Specifies a date and time on which a trigger will start.</summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
/// <param name="hour">The hour.</param>
/// <param name="min">The min.</param>
/// <param name="sec">The sec.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Starting(int year, int month, int day, int hour, int min, int sec)
{
trigger.StartBoundary = new DateTime(year, month, day, hour, min, sec);
return this;
}
/// <summary>Specifies a date and time on which a trigger will start.</summary>
/// <param name="dt">A string representing a DateTime and parsable via <see cref="DateTime.Parse(string)"/>.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Starting([NotNull] string dt)
{
trigger.StartBoundary = DateTime.Parse(dt);
return this;
}
/// <summary>Specifies a date and time on which a trigger will start.</summary>
/// <param name="dt">The DateTime value.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder Starting(DateTime dt)
{
trigger.StartBoundary = dt;
return this;
}
}
/// <summary>Fluent helper class. Not intended for use.</summary>
public class WeeklyTriggerBuilder : TriggerBuilder
{
internal WeeklyTriggerBuilder(BuilderInfo taskBuilder, short interval)
: base(taskBuilder) => TaskDef.Triggers.Add(trigger = new WeeklyTrigger() { WeeksInterval = interval });
/// <summary>Updates a weekly trigger to specify the days of the week on which it will run.</summary>
/// <param name="dow">The days of the week.</param>
/// <returns><see cref="TriggerBuilder"/> instance.</returns>
public TriggerBuilder On(DaysOfTheWeek dow)
{
((WeeklyTrigger)trigger).DaysOfWeek = dow;
return this as TriggerBuilder;
}
}
/// <summary>Fluent helper class. Not intended for use.</summary>
internal sealed class BuilderInfo
{
public TaskDefinition td;
public TaskService ts;
public BuilderInfo([NotNull] TaskService taskSvc)
{
ts = taskSvc;
td = ts.NewTask();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,534 @@
using JetBrains.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
namespace Microsoft.Win32.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, INotifyCollectionChanged, INotifyPropertyChanged
{
private const string IndexerName = "Item[]";
private readonly V2Interop.ITriggerCollection v2Coll;
private bool inV2set;
private V1Interop.ITask v1Task;
private V2Interop.ITaskDefinition v2Def;
internal TriggerCollection([NotNull] V1Interop.ITask iTask) => v1Task = iTask;
internal TriggerCollection([NotNull] V2Interop.ITaskDefinition iTaskDef)
{
v2Def = iTaskDef;
v2Coll = v2Def.Triggers;
}
/// <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;
/// <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
{
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>
/// <param name="index">The index of the trigger to be retrieved.</param>
/// <returns>Specialized <see cref="Trigger"/> instance.</returns>
public Trigger this[int index]
{
get
{
if (v2Coll != null)
return Trigger.CreateTrigger(v2Coll[++index], v2Def);
return Trigger.CreateTrigger(v1Task.GetTrigger((ushort)index));
}
set
{
if (index < 0 || Count <= index)
throw new ArgumentOutOfRangeException(nameof(index), index, @"Index is not a valid index in the TriggerCollection");
var orig = this[index].Clone();
inV2set = true;
try
{
Insert(index, value);
RemoveAt(index + 1);
}
finally
{
inV2set = false;
}
OnNotifyPropertyChanged(IndexerName);
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, orig, index));
}
}
object IList.this[int index]
{
get => this[index];
set => this[index] = (Trigger)value;
}
/*/// <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));
if (v2Def != null)
unboundTrigger.Bind(v2Def);
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>
/// <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)
return Trigger.CreateTrigger(v1Task.CreateTrigger(out _), Trigger.ConvertToV1TriggerType(taskTriggerType));
return Trigger.CreateTrigger(v2Coll.Create(taskTriggerType), v2Def);
}
/// <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
{
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="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>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="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="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="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)
throw new ArgumentNullException(nameof(array));
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException(nameof(index));
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
if (count < 0 || count > (Count - index))
throw new ArgumentOutOfRangeException(nameof(count));
if ((Count - index) > (array.Length - arrayIndex))
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
for (var i = 0; i < count; i++)
array[arrayIndex + i] = (Trigger)this[index + i].Clone();
}
/// <summary>Releases all resources used by this class.</summary>
public void Dispose()
{
if (v2Coll != null) Marshal.ReleaseComObject(v2Coll);
v2Def = null;
v1Task = null;
}
/// <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.
/// </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>
public Trigger Find([NotNull] Predicate<Trigger> match)
{
if (match == null)
throw new ArgumentNullException(nameof(match));
foreach (var item in this)
if (match(item)) return item;
return null;
}
/// <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.
/// </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>
public int FindIndexOf(int startIndex, int count, [NotNull] Predicate<Trigger> match)
{
if (startIndex < 0 || startIndex >= Count)
throw new ArgumentOutOfRangeException(nameof(startIndex));
if (startIndex + count > Count)
throw new ArgumentOutOfRangeException(nameof(count));
if (match == null)
throw new ArgumentNullException(nameof(match));
for (var i = startIndex; i < startIndex + count; i++)
if (match(this[i])) return i;
return -1;
}
/// <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.
/// </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>
public int FindIndexOf([NotNull] Predicate<Trigger> match) => FindIndexOf(0, Count, match);
/// <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, 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)
throw new RankException("Multi-dimensional arrays are not supported.");
var src = new Trigger[Count];
CopyTo(src, 0);
Array.Copy(src, 0, array, index, Count);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() => null;
int IList.IndexOf(object value) => IndexOf((Trigger)value);
void IList.Insert(int index, object value) => Insert(index, (Trigger)value);
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
reader.ReadStartElement(XmlSerializationHelper.GetElementName(this), TaskDefinition.tns);
while (reader.MoveToContent() == System.Xml.XmlNodeType.Element)
{
switch (reader.LocalName)
{
case "BootTrigger":
XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Boot));
break;
case "IdleTrigger":
XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Idle));
break;
case "TimeTrigger":
XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Time));
break;
case "LogonTrigger":
XmlSerializationHelper.ReadObject(reader, AddNew(TaskTriggerType.Logon));
break;
case "CalendarTrigger":
Add(CalendarTrigger.GetTriggerFromXml(reader));
break;
default:
reader.Skip();
break;
}
}
reader.ReadEndElement();
}
void IList.Remove(object value) => Remove((Trigger)value);
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
foreach (var t in this)
XmlSerializationHelper.WriteObject(writer, t);
}
internal void Bind()
{
foreach (var t in this)
t.SetV1TriggerData();
}
/// <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 V1Interop.ITask iTask;
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;
public bool MoveNext() => (++curItem < iTask.GetTriggerCount());
public void Reset() => curItem = -1;
}
}
}

View File

@ -0,0 +1,146 @@
using System;
using System.Security.Principal;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>Represents a system account.</summary>
internal class User : IEquatable<User>, IDisposable
{
private static readonly WindowsIdentity cur = WindowsIdentity.GetCurrent();
private SecurityIdentifier sid;
/// <summary>Initializes a new instance of the <see cref="User"/> class.</summary>
/// <param name="userName">
/// Name of the user. This can be in the format <c>DOMAIN\username</c> or <c>username@domain.com</c> or <c>username</c> or
/// <c>null</c> (for current user).
/// </param>
public User(string userName = null)
{
if (string.IsNullOrEmpty(userName)) userName = null;
// 2018-03-02: Hopefully not a breaking change, but by adding in the comparison of an account name without a domain and the
// current user, there is a chance that current implementations will break given the condition that a local account with the same
// name as a domain account exists and the intention was to prefer the local account. In such a case, the developer should
// prepend the user name in TaskDefinition.Principal.UserId with the machine name of the local machine.
if (userName == null || cur.Name.Equals(userName, StringComparison.InvariantCultureIgnoreCase) || GetUser(cur.Name).Equals(userName, StringComparison.InvariantCultureIgnoreCase))
{
Identity = cur;
sid = Identity.User;
}
else if (userName.Contains("\\") && !userName.StartsWith(@"NT AUTHORITY\"))
{
try
{
using (var ds = new NativeMethods.DomainService())
{
Identity = new WindowsIdentity(ds.CrackName(userName));
sid = Identity.User;
}
}
catch { }
}
if (Identity == null)
{
if (userName != null && userName.Contains("@"))
{
Identity = new WindowsIdentity(userName);
sid = Identity.User;
}
if (Identity == null && userName != null)
{
var ntacct = new NTAccount(userName);
try { sid = (SecurityIdentifier)ntacct.Translate(typeof(SecurityIdentifier)); } catch { }
}
}
string GetUser(string domUser)
{
var split = domUser.Split('\\');
return split.Length == 2 ? split[1] : domUser;
}
}
/// <summary>Initializes a new instance of the <see cref="User"/> class.</summary>
/// <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
{
get
{
try
{
return (sid != null && (sid.IsWellKnown(WellKnownSidType.LocalSystemSid) || sid.IsWellKnown(WellKnownSidType.NetworkServiceSid) || sid.IsWellKnown(WellKnownSidType.LocalServiceSid)));
}
catch { }
return false;
}
}
/// <summary>Gets a value indicating whether this instance is the SYSTEM account.</summary>
/// <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;
/// <summary>Create a <see cref="User"/> instance from a SID string.</summary>
/// <param name="sid">The SID string.</param>
/// <returns>A <see cref="User"/> instance.</returns>
public static User FromSidString(string sid) => new User(((NTAccount)new SecurityIdentifier(sid).Translate(typeof(NTAccount))).Value);
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose() => Identity?.Dispose();
/// <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>
public override bool Equals(object obj)
{
if (obj is User user)
return Equals(user);
if (obj is WindowsIdentity wid && sid != null)
return sid.Equals(wid.User);
try
{
if (obj is string un)
return Equals(new User(un));
}
catch { }
return base.Equals(obj);
}
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
public bool Equals(User other) => (other != null && sid != null) ? sid.Equals(other.sid) : false;
/// <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() => sid?.GetHashCode() ?? 0;
}
}

View File

@ -0,0 +1,472 @@
using System;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
// ReSharper disable InconsistentNaming
// ReSharper disable FieldCanBeMadeReadOnly.Global
namespace Microsoft.Win32.TaskScheduler.V1Interop
{
#pragma warning disable CS0618 // Type or member is obsolete
#region class HRESULT -- Values peculiar to the task scheduler.
internal class HResult
{
// The task is ready to run at its next scheduled time.
public const int SCHED_S_TASK_READY = 0x00041300;
// The task is currently running.
public const int SCHED_S_TASK_RUNNING = 0x00041301;
// The task will not run at the scheduled times because it has been disabled.
public const int SCHED_S_TASK_DISABLED = 0x00041302;
// The task has not yet run.
public const int SCHED_S_TASK_HAS_NOT_RUN = 0x00041303;
// There are no more runs scheduled for this task.
public const int SCHED_S_TASK_NO_MORE_RUNS = 0x00041304;
// One or more of the properties that are needed to run this task on a schedule have not been set.
public const int SCHED_S_TASK_NOT_SCHEDULED = 0x00041305;
// The last run of the task was terminated by the user.
public const int SCHED_S_TASK_TERMINATED = 0x00041306;
// Either the task has no triggers or the existing triggers are disabled or not set.
public const int SCHED_S_TASK_NO_VALID_TRIGGERS = 0x00041307;
// Event triggers don't have set run times.
public const int SCHED_S_EVENT_TRIGGER = 0x00041308;
// Trigger not found.
public const int SCHED_E_TRIGGER_NOT_FOUND = unchecked((int)0x80041309);
// One or more of the properties that are needed to run this task have not been set.
public const int SCHED_E_TASK_NOT_READY = unchecked((int)0x8004130A);
// There is no running instance of the task to terminate.
public const int SCHED_E_TASK_NOT_RUNNING = unchecked((int)0x8004130B);
// The Task Scheduler Service is not installed on this computer.
public const int SCHED_E_SERVICE_NOT_INSTALLED = unchecked((int)0x8004130C);
// The task object could not be opened.
public const int SCHED_E_CANNOT_OPEN_TASK = unchecked((int)0x8004130D);
// The object is either an invalid task object or is not a task object.
public const int SCHED_E_INVALID_TASK = unchecked((int)0x8004130E);
// No account information could be found in the Task Scheduler security database for the task indicated.
public const int SCHED_E_ACCOUNT_INFORMATION_NOT_SET = unchecked((int)0x8004130F);
// Unable to establish existence of the account specified.
public const int SCHED_E_ACCOUNT_NAME_NOT_FOUND = unchecked((int)0x80041310);
// Corruption was detected in the Task Scheduler security database; the database has been reset.
public const int SCHED_E_ACCOUNT_DBASE_CORRUPT = unchecked((int)0x80041311);
// Task Scheduler security services are available only on Windows NT.
public const int SCHED_E_NO_SECURITY_SERVICES = unchecked((int)0x80041312);
// The task object version is either unsupported or invalid.
public const int SCHED_E_UNKNOWN_OBJECT_VERSION = unchecked((int)0x80041313);
// The task has been configured with an unsupported combination of account settings and run time options.
public const int SCHED_E_UNSUPPORTED_ACCOUNT_OPTION = unchecked((int)0x80041314);
// The Task Scheduler Service is not running.
public const int SCHED_E_SERVICE_NOT_RUNNING = unchecked((int)0x80041315);
// The Task Scheduler service must be configured to run in the System account to function properly. Individual tasks may be configured to run in other accounts.
public const int SCHED_E_SERVICE_NOT_LOCALSYSTEM = unchecked((int)0x80041316);
}
#endregion
#region Enums
/// <summary>
/// Options for a task, used for the Flags property of a Task. Uses the
/// "Flags" attribute, so these values are combined with |.
/// Some flags are documented as Windows 95 only, but they have a
/// user interface in Windows XP so that may not be true.
/// </summary>
[Flags]
internal enum TaskFlags
{
/// <summary>
/// The interactive flag is set if the task is intended to be displayed to the user.
/// If the flag is not set, no user interface associated with the task is presented
/// to the user when the task is executed.
/// </summary>
Interactive = 0x1,
/// <summary>
/// The task will be deleted when there are no more scheduled run times.
/// </summary>
DeleteWhenDone = 0x2,
/// <summary>
/// The task is disabled. This is useful to temporarily prevent a task from running
/// at the scheduled time(s).
/// </summary>
Disabled = 0x4,
/// <summary>
/// The task begins only if the computer is not in use at the scheduled start time. Windows 95 only.
/// </summary>
StartOnlyIfIdle = 0x10,
/// <summary>
/// The task terminates if the computer makes an idle to non-idle transition while the task is running.
/// The computer is not considered idle until the IdleWait triggers' time elapses with no user input.
/// Windows 95 only. For information regarding idle triggers, see <see cref="IdleTrigger"/>.
/// </summary>
KillOnIdleEnd = 0x20,
/// <summary>
/// The task does not start if its target computer is running on battery power. Windows 95 only.
/// </summary>
DontStartIfOnBatteries = 0x40,
/// <summary>
/// The task ends, and the associated application quits if the task's target computer switches
/// to battery power. Windows 95 only.
/// </summary>
KillIfGoingOnBatteries = 0x80,
/// <summary>
/// The task runs only if the system is docked. Windows 95 only.
/// </summary>
RunOnlyIfDocked = 0x100,
/// <summary>
/// The work item created will be hidden.
/// </summary>
Hidden = 0x200,
/// <summary>
/// The task runs only if there is currently a valid Internet connection.
/// This feature is currently not implemented.
/// </summary>
RunIfConnectedToInternet = 0x400,
/// <summary>
/// The task starts again if the computer makes a non-idle to idle transition before all the
/// task's task_triggers elapse. (Use this flag in conjunction with KillOnIdleEnd.) Windows 95 only.
/// </summary>
RestartOnIdleResume = 0x800,
/// <summary>
/// The task runs only if the SYSTEM account is available.
/// </summary>
SystemRequired = 0x1000,
/// <summary>
/// The task runs only if the user specified in SetAccountInformation is logged on interactively.
/// This flag has no effect on work items set to run in the local account.
/// </summary>
RunOnlyIfLoggedOn = 0x2000
}
/// <summary>
/// Status values returned for a task. Some values have been determined to occur although
/// they do no appear in the Task Scheduler system documentation.
/// </summary>
internal enum TaskStatus
{
/// <summary>The task is ready to run at its next scheduled time.</summary>
Ready = HResult.SCHED_S_TASK_READY,
/// <summary>The task is currently running.</summary>
Running = HResult.SCHED_S_TASK_RUNNING,
/// <summary>One or more of the properties that are needed to run this task on a schedule have not been set. </summary>
NotScheduled = HResult.SCHED_S_TASK_NOT_SCHEDULED,
/// <summary>The task has not yet run.</summary>
NeverRun = HResult.SCHED_S_TASK_HAS_NOT_RUN,
/// <summary>The task will not run at the scheduled times because it has been disabled.</summary>
Disabled = HResult.SCHED_S_TASK_DISABLED,
/// <summary>There are no more runs scheduled for this task.</summary>
NoMoreRuns = HResult.SCHED_S_TASK_NO_MORE_RUNS,
/// <summary>The last run of the task was terminated by the user.</summary>
Terminated = HResult.SCHED_S_TASK_TERMINATED,
/// <summary>Either the task has no triggers or the existing triggers are disabled or not set.</summary>
NoTriggers = HResult.SCHED_S_TASK_NO_VALID_TRIGGERS,
/// <summary>Event triggers don't have set run times.</summary>
NoTriggerTime = HResult.SCHED_S_EVENT_TRIGGER
}
/// <summary>Valid types of triggers</summary>
internal enum TaskTriggerType
{
/// <summary>Trigger is set to run the task a single time. </summary>
RunOnce = 0,
/// <summary>Trigger is set to run the task on a daily interval. </summary>
RunDaily = 1,
/// <summary>Trigger is set to run the work item on specific days of a specific week of a specific month. </summary>
RunWeekly = 2,
/// <summary>Trigger is set to run the task on a specific day(s) of the month.</summary>
RunMonthly = 3,
/// <summary>Trigger is set to run the task on specific days, weeks, and months.</summary>
RunMonthlyDOW = 4,
/// <summary>Trigger is set to run the task if the system remains idle for the amount of time specified by the idle wait time of the task.</summary>
OnIdle = 5,
/// <summary>Trigger is set to run the task at system startup.</summary>
OnSystemStart = 6,
/// <summary>Trigger is set to run the task when a user logs on. </summary>
OnLogon = 7
}
[Flags]
internal enum TaskTriggerFlags : uint
{
HasEndDate = 0x1,
KillAtDurationEnd = 0x2,
Disabled = 0x4
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
internal struct Daily
{
public ushort DaysInterval;
}
[StructLayout(LayoutKind.Sequential)]
internal struct Weekly
{
public ushort WeeksInterval;
public DaysOfTheWeek DaysOfTheWeek;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MonthlyDate
{
public uint Days;
public MonthsOfTheYear Months;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MonthlyDOW
{
public ushort WhichWeek;
public DaysOfTheWeek DaysOfTheWeek;
public MonthsOfTheYear Months;
public WhichWeek V2WhichWeek
{
get
{
return (WhichWeek)(1 << ((short)WhichWeek - 1));
}
set
{
int idx = Array.IndexOf(new short[] { 0x1, 0x2, 0x4, 0x8, 0x10 }, (short)value);
if (idx >= 0)
WhichWeek = (ushort)(idx + 1);
else
throw new NotV1SupportedException("Only a single week can be set with Task Scheduler 1.0.");
}
}
}
[StructLayout(LayoutKind.Explicit)]
internal struct TriggerTypeData
{
[FieldOffset(0)]
public Daily daily;
[FieldOffset(0)]
public Weekly weekly;
[FieldOffset(0)]
public MonthlyDate monthlyDate;
[FieldOffset(0)]
public MonthlyDOW monthlyDOW;
}
[StructLayout(LayoutKind.Sequential)]
internal struct TaskTrigger
{
public ushort TriggerSize; // Structure size.
public ushort Reserved1; // Reserved. Must be zero.
public ushort BeginYear; // Trigger beginning date year.
public ushort BeginMonth; // Trigger beginning date month.
public ushort BeginDay; // Trigger beginning date day.
public ushort EndYear; // Optional trigger ending date year.
public ushort EndMonth; // Optional trigger ending date month.
public ushort EndDay; // Optional trigger ending date day.
public ushort StartHour; // Run bracket start time hour.
public ushort StartMinute; // Run bracket start time minute.
public uint MinutesDuration; // Duration of run bracket.
public uint MinutesInterval; // Run bracket repetition interval.
public TaskTriggerFlags Flags; // Trigger flags.
public TaskTriggerType Type; // Trigger type.
public TriggerTypeData Data; // Trigger data peculiar to this type (union).
public ushort Reserved2; // Reserved. Must be zero.
public ushort RandomMinutesInterval; // Maximum number of random minutes after start time.
public DateTime BeginDate
{
get { try { return BeginYear == 0 ? DateTime.MinValue : new DateTime(BeginYear, BeginMonth, BeginDay, StartHour, StartMinute, 0, DateTimeKind.Unspecified); } catch { return DateTime.MinValue; } }
set
{
if (value != DateTime.MinValue)
{
DateTime local = value.Kind == DateTimeKind.Utc ? value.ToLocalTime() : value;
BeginYear = (ushort)local.Year;
BeginMonth = (ushort)local.Month;
BeginDay = (ushort)local.Day;
StartHour = (ushort)local.Hour;
StartMinute = (ushort)local.Minute;
}
else
BeginYear = BeginMonth = BeginDay = StartHour = StartMinute = 0;
}
}
public DateTime? EndDate
{
get { try { return EndYear == 0 ? (DateTime?)null : new DateTime(EndYear, EndMonth, EndDay); } catch { return DateTime.MaxValue; } }
set
{
if (value.HasValue)
{
EndYear = (ushort)value.Value.Year;
EndMonth = (ushort)value.Value.Month;
EndDay = (ushort)value.Value.Day;
Flags |= TaskTriggerFlags.HasEndDate;
}
else
{
EndYear = EndMonth = EndDay = 0;
Flags &= ~TaskTriggerFlags.HasEndDate;
}
}
}
public override string ToString() => $"Trigger Type: {Type};\n> Start: {BeginDate}; End: {(EndYear == 0 ? "null" : EndDate?.ToString())};\n> DurMin: {MinutesDuration}; DurItv: {MinutesInterval};\n>";
}
#endregion
[ComImport, Guid("148BD527-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTaskScheduler))]
internal interface ITaskScheduler
{
void SetTargetComputer([In, MarshalAs(UnmanagedType.LPWStr)] string Computer);
CoTaskMemString GetTargetComputer();
[return: MarshalAs(UnmanagedType.Interface)]
IEnumWorkItems Enum();
[return: MarshalAs(UnmanagedType.Interface)]
ITask Activate([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
void Delete([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name);
[return: MarshalAs(UnmanagedType.Interface)]
ITask NewWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
void AddWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.Interface)] ITask WorkItem);
void IsOfType([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
}
[Guid("148BD528-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IEnumWorkItems
{
[PreserveSig()]
//int Next([In] uint celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] out string[] rgpwszNames, [Out] out uint pceltFetched);
int Next([In] uint RequestCount, [Out] out IntPtr Names, [Out] out uint Fetched);
void Skip([In] uint Count);
void Reset();
[return: MarshalAs(UnmanagedType.Interface)]
IEnumWorkItems Clone();
}
#if WorkItem
// The IScheduledWorkItem interface is actually never used because ITask inherits all of its
// methods. As ITask is the only kind of WorkItem (in 2002) it is the only interface we need.
[Guid("a6b952f0-a4b1-11d0-997d-00aa006887ec"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IScheduledWorkItem
{
void CreateTrigger([Out] out ushort NewTriggerIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITaskTrigger Trigger);
void DeleteTrigger([In] ushort TriggerIndex);
void GetTriggerCount([Out] out ushort Count);
void GetTrigger([In] ushort TriggerIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITaskTrigger Trigger);
void GetTriggerString([In] ushort TriggerIndex, out System.IntPtr TriggerString);
void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] SystemTime Begin, [In, MarshalAs(UnmanagedType.Struct)] SystemTime End, ref ushort Count, [Out] out System.IntPtr TaskTimes);
void GetNextRunTime([In, Out, MarshalAs(UnmanagedType.Struct)] ref SystemTime NextRun);
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] uint hParent, [In] uint dwReserved);
void GetMostRecentRunTime([In, Out, MarshalAs(UnmanagedType.Struct)] ref SystemTime LastRun);
void GetStatus([Out, MarshalAs(UnmanagedType.Error)] out int Status);
void GetExitCode([Out] out uint ExitCode);
void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment);
void GetComment(out System.IntPtr Comment);
void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator);
void GetCreator(out System.IntPtr Creator);
void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0, ArraySubType=UnmanagedType.U1)] byte[] Data);
void GetWorkItemData([Out] out ushort DataLen, [Out] out System.IntPtr Data);
void SetErrorRetryCount([In] ushort RetryCount);
void GetErrorRetryCount([Out] out ushort RetryCount);
void SetErrorRetryInterval([In] ushort RetryInterval);
void GetErrorRetryInterval([Out] out ushort RetryInterval);
void SetFlags([In] uint Flags);
void GetFlags([Out] out uint Flags);
void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In, MarshalAs(UnmanagedType.LPWStr)] string Password);
void GetAccountInformation(out System.IntPtr AccountName);
}
#endif
[ComImport, Guid("148BD524-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTask))]
internal interface ITask
{
[return: MarshalAs(UnmanagedType.Interface)]
ITaskTrigger CreateTrigger([Out] out ushort NewTriggerIndex);
void DeleteTrigger([In] ushort TriggerIndex);
[return: MarshalAs(UnmanagedType.U2)]
ushort GetTriggerCount();
[return: MarshalAs(UnmanagedType.Interface)]
ITaskTrigger GetTrigger([In] ushort TriggerIndex);
CoTaskMemString GetTriggerString([In] ushort TriggerIndex);
void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME Begin, [In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME End, ref ushort Count, [In, Out] ref IntPtr TaskTimes);
[return: MarshalAs(UnmanagedType.Struct)]
NativeMethods.SYSTEMTIME GetNextRunTime();
void SetIdleWait([In] ushort IdleMinutes, [In] ushort DeadlineMinutes);
void GetIdleWait([Out] out ushort IdleMinutes, [Out] out ushort DeadlineMinutes);
void Run();
void Terminate();
void EditWorkItem([In] IntPtr hParent, [In] uint dwReserved);
[return: MarshalAs(UnmanagedType.Struct)]
NativeMethods.SYSTEMTIME GetMostRecentRunTime();
TaskStatus GetStatus();
uint GetExitCode();
void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment);
CoTaskMemString GetComment();
void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator);
CoTaskMemString GetCreator();
void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U1)] byte[] Data);
void GetWorkItemData(out ushort DataLen, [Out] out IntPtr Data);
void SetErrorRetryCount([In] ushort RetryCount);
ushort GetErrorRetryCount();
void SetErrorRetryInterval([In] ushort RetryInterval);
ushort GetErrorRetryInterval();
void SetFlags([In] TaskFlags Flags);
TaskFlags GetFlags();
void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In] IntPtr Password);
CoTaskMemString GetAccountInformation();
void SetApplicationName([In, MarshalAs(UnmanagedType.LPWStr)] string ApplicationName);
CoTaskMemString GetApplicationName();
void SetParameters([In, MarshalAs(UnmanagedType.LPWStr)] string Parameters);
CoTaskMemString GetParameters();
void SetWorkingDirectory([In, MarshalAs(UnmanagedType.LPWStr)] string WorkingDirectory);
CoTaskMemString GetWorkingDirectory();
void SetPriority([In] uint Priority);
uint GetPriority();
void SetTaskFlags([In] uint Flags);
uint GetTaskFlags();
void SetMaxRunTime([In] uint MaxRunTimeMS);
uint GetMaxRunTime();
}
[Guid("148BD52B-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskTrigger
{
void SetTrigger([In, Out, MarshalAs(UnmanagedType.Struct)] ref TaskTrigger Trigger);
[return: MarshalAs(UnmanagedType.Struct)]
TaskTrigger GetTrigger();
CoTaskMemString GetTriggerString();
}
[ComImport, Guid("148BD52A-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)]
internal class CTaskScheduler
{
}
[ComImport, Guid("148BD520-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)]
internal class CTask
{
}
internal sealed class CoTaskMemString : SafeHandle
{
public CoTaskMemString() : base(IntPtr.Zero, true) { }
public CoTaskMemString(IntPtr handle) : this() { SetHandle(handle); }
public CoTaskMemString(string text) : this() { SetHandle(Marshal.StringToCoTaskMemUni(text)); }
public static implicit operator string (CoTaskMemString cmem) => cmem.ToString();
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
Marshal.FreeCoTaskMem(handle);
return true;
}
public override string ToString() => Marshal.PtrToStringUni(handle);
}
}

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

@ -0,0 +1,577 @@
using System;
using System.Collections;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace Microsoft.Win32.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; }
}
[ComImport, Guid("02820E19-7B98-4ED2-B2E8-FDCCCEFF619B"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IActionCollection
{
int Count { get; }
IAction this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
[return: MarshalAs(UnmanagedType.Interface)]
IAction Create([In] TaskActionType Type);
void Remove([In, MarshalAs(UnmanagedType.Struct)][NotNull] object index);
void Clear();
string Context { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("2A9C35DA-D357-41F4-BBC1-207AC1B1F3CB"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IBootTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("6D2FD252-75C5-4F66-90BA-2A7D8CC3039F"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IComHandlerAction : IAction
{
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new TaskActionType Type { get; }
string ClassId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Data { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("126C5CD8-B288-41D5-8DBF-E491446ADC5C"), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IDailyTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
short DaysInterval { get; [param: In] set; }
string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("10F62C64-7E16-4314-A0C2-0C3683F99D40"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IEmailAction : IAction
{
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new TaskActionType Type { get; }
string Server { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Subject { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string To { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Cc { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Bcc { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string ReplyTo { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string From { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
ITaskNamedValueCollection HeaderFields { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
string Body { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
object[] Attachments { [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] get; [param: In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] set; }
}
[ComImport, Guid("D45B0167-9653-4EEF-B94F-0732CA7AF251"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IEventTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
string Subscription { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
ITaskNamedValueCollection ValueQueries { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
}
[ComImport, Guid("4C3D624D-FD6B-49A3-B9B7-09CB3CD3F047"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IExecAction : IAction
{
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new TaskActionType Type { get; }
string Path { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Arguments { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("84594461-0053-4342-A8FD-088FABF11F32"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IIdleSettings
{
string IdleDuration { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string WaitTimeout { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
bool StopOnIdleEnd { get; [param: In] set; }
bool RestartOnIdle { get; [param: In] set; }
}
[ComImport, Guid("D537D2B0-9FB3-4D34-9739-1FF5CE7B1EF3"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IIdleTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
}
[ComImport, Guid("72DADE38-FAE4-4B3E-BAF4-5D009AF02B1C"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ILogonTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("77D025A3-90FA-43AA-B52E-CDA5499B946A"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IMonthlyDOWTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
short DaysOfWeek { get; [param: In] set; }
short WeeksOfMonth { get; [param: In] set; }
short MonthsOfYear { get; [param: In] set; }
bool RunOnLastWeekOfMonth { get; [param: In] set; }
string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("97C45EF1-6B02-4A1A-9C0E-1EBFBA1500AC"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IMonthlyTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
int DaysOfMonth { get; [param: In] set; }
short MonthsOfYear { get; [param: In] set; }
bool RunOnLastDayOfMonth { get; [param: In] set; }
string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("9F7DEA84-C30B-4245-80B6-00E9F646F1B4"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface INetworkSettings
{
string Name { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("D98D51E5-C9B4-496A-A9C1-18980261CF0F"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IPrincipal
{
string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string DisplayName { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
TaskLogonType LogonType { get; set; }
string GroupId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
TaskRunLevel RunLevel { get; set; }
}
[ComImport, Guid("248919AE-E345-4A6D-8AEB-E0D3165C904E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IPrincipal2
{
TaskProcessTokenSidType ProcessTokenSidType { get; [param: In] set; }
int RequiredPrivilegeCount { get; }
string this[int index] { [return: MarshalAs(UnmanagedType.BStr)] get; }
void AddRequiredPrivilege([In, MarshalAs(UnmanagedType.BStr)] string privilege);
}
[ComImport, Guid("9C86F320-DEE3-4DD1-B972-A303F26B061E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Path")]
internal interface IRegisteredTask
{
string Name { [return: MarshalAs(UnmanagedType.BStr)] get; }
string Path { [return: MarshalAs(UnmanagedType.BStr)] get; }
TaskState State { get; }
bool Enabled { get; set; }
[return: MarshalAs(UnmanagedType.Interface)]
IRunningTask Run([In, MarshalAs(UnmanagedType.Struct)] object parameters);
[return: MarshalAs(UnmanagedType.Interface)]
IRunningTask RunEx([In, MarshalAs(UnmanagedType.Struct)] object parameters, [In] int flags, [In] int sessionID, [In, MarshalAs(UnmanagedType.BStr)] string user);
[return: MarshalAs(UnmanagedType.Interface)]
IRunningTaskCollection GetInstances(int flags);
DateTime LastRunTime { get; }
int LastTaskResult { get; }
int NumberOfMissedRuns { get; }
DateTime NextRunTime { get; }
ITaskDefinition Definition { [return: MarshalAs(UnmanagedType.Interface)] get; }
string Xml { [return: MarshalAs(UnmanagedType.BStr)] get; }
[return: MarshalAs(UnmanagedType.BStr)]
string GetSecurityDescriptor(int securityInformation);
void SetSecurityDescriptor([In, MarshalAs(UnmanagedType.BStr)] string sddl, [In] int flags);
void Stop(int flags);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x60020011)]
void GetRunTimes([In] ref Microsoft.Win32.NativeMethods.SYSTEMTIME pstStart, [In] ref Microsoft.Win32.NativeMethods.SYSTEMTIME pstEnd, [In, Out] ref uint pCount, [In, Out] ref IntPtr pRunTimes);
}
[ComImport, Guid("86627EB4-42A7-41E4-A4D9-AC33A72F2D52"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IRegisteredTaskCollection
{
int Count { get; }
IRegisteredTask this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
}
[ComImport, Guid("416D8B73-CB41-4EA1-805C-9BE9A5AC4A74"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IRegistrationInfo
{
string Description { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Author { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Version { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Date { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Documentation { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string URI { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
object SecurityDescriptor { [return: MarshalAs(UnmanagedType.Struct)] get; [param: In, MarshalAs(UnmanagedType.Struct)] set; }
string Source { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("4C8FEC3A-C218-4E0C-B23D-629024DB91A2"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IRegistrationTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("7FB9ACF1-26BE-400E-85B5-294B9C75DFD6"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IRepetitionPattern
{
string Interval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Duration { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
bool StopAtDurationEnd { get; [param: In] set; }
}
[ComImport, Guid("653758FB-7B9A-4F1E-A471-BEEB8E9B834E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("InstanceGuid")]
internal interface IRunningTask
{
string Name { [return: MarshalAs(UnmanagedType.BStr)] get; }
string InstanceGuid { [return: MarshalAs(UnmanagedType.BStr)] get; }
string Path { [return: MarshalAs(UnmanagedType.BStr)] get; }
TaskState State { get; }
string CurrentAction { [return: MarshalAs(UnmanagedType.BStr)] get; }
void Stop();
void Refresh();
uint EnginePID { get; }
}
[ComImport, Guid("6A67614B-6828-4FEC-AA54-6D52E8F1F2DB"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IRunningTaskCollection
{
int Count { get; }
IRunningTask this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("754DA71B-4385-4475-9DD9-598294FA3641"), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ISessionStateChangeTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
string Delay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string UserId { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
TaskSessionStateChangeType StateChange { get; [param: In] set; }
}
[ComImport, Guid("505E9E68-AF89-46B8-A30F-56162A83D537"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IShowMessageAction : IAction
{
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new TaskActionType Type { get; }
string Title { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string MessageBody { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("F5BC8FC5-536D-4F77-B852-FBC1356FDEB6"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskDefinition
{
IRegistrationInfo RegistrationInfo { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
ITriggerCollection Triggers { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
ITaskSettings Settings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
string Data { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
IPrincipal Principal { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
IActionCollection Actions { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("8CFAC062-A080-4C15-9A88-AA7C2AF80DFC"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Path")]
internal interface ITaskFolder
{
string Name { [return: MarshalAs(UnmanagedType.BStr)] get; }
string Path { [return: MarshalAs(UnmanagedType.BStr)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
ITaskFolder GetFolder([MarshalAs(UnmanagedType.BStr)] string Path);
[return: MarshalAs(UnmanagedType.Interface)]
ITaskFolderCollection GetFolders(int flags);
[return: MarshalAs(UnmanagedType.Interface)]
ITaskFolder CreateFolder([In, MarshalAs(UnmanagedType.BStr)] string subFolderName, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl);
void DeleteFolder([MarshalAs(UnmanagedType.BStr)] string subFolderName, [In] int flags);
[return: MarshalAs(UnmanagedType.Interface)]
IRegisteredTask GetTask([MarshalAs(UnmanagedType.BStr)][NotNull] string Path);
[return: MarshalAs(UnmanagedType.Interface)]
IRegisteredTaskCollection GetTasks(int flags);
void DeleteTask([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Name, [In] int flags);
[return: MarshalAs(UnmanagedType.Interface)]
IRegisteredTask RegisterTask([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path, [In, MarshalAs(UnmanagedType.BStr)][NotNull] string XmlText, [In] int flags, [In, MarshalAs(UnmanagedType.Struct)] object UserId, [In, MarshalAs(UnmanagedType.Struct)] object password, [In] TaskLogonType LogonType, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl);
[return: MarshalAs(UnmanagedType.Interface)]
IRegisteredTask RegisterTaskDefinition([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path, [In, MarshalAs(UnmanagedType.Interface)][NotNull] ITaskDefinition pDefinition, [In] int flags, [In, MarshalAs(UnmanagedType.Struct)] object UserId, [In, MarshalAs(UnmanagedType.Struct)] object password, [In] TaskLogonType LogonType, [In, Optional, MarshalAs(UnmanagedType.Struct)] object sddl);
[return: MarshalAs(UnmanagedType.BStr)]
string GetSecurityDescriptor(int securityInformation);
void SetSecurityDescriptor([In, MarshalAs(UnmanagedType.BStr)] string sddl, [In] int flags);
}
[ComImport, Guid("79184A66-8664-423F-97F1-637356A5D812"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskFolderCollection
{
int Count { get; }
ITaskFolder this[object index] { [return: MarshalAs(UnmanagedType.Interface)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
}
[ComImport, Guid("B4EF826B-63C3-46E4-A504-EF69E4F7EA4D"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskNamedValueCollection
{
int Count { get; }
ITaskNamedValuePair this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
ITaskNamedValuePair Create([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.BStr)] string Value);
void Remove([In] int index);
void Clear();
}
[ComImport, Guid("39038068-2B46-4AFD-8662-7BB6F868D221"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity, DefaultMember("Name")]
internal interface ITaskNamedValuePair
{
string Name { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string Value { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, DefaultMember("TargetServer"), Guid("2FABA4C7-4DA9-4013-9697-20CC3FD40F85"), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(TaskSchedulerClass))]
internal interface ITaskService
{
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)]
ITaskFolder GetFolder([In, MarshalAs(UnmanagedType.BStr)][NotNull] string Path);
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)]
IRunningTaskCollection GetRunningTasks(int flags);
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)]
ITaskDefinition NewTask([In] uint flags);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)]
void Connect([In, Optional, MarshalAs(UnmanagedType.Struct)] object serverName, [In, Optional, MarshalAs(UnmanagedType.Struct)] object user, [In, Optional, MarshalAs(UnmanagedType.Struct)] object domain, [In, Optional, MarshalAs(UnmanagedType.Struct)] object password);
[DispId(5)]
bool Connected { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(5)] get; }
[DispId(0)]
string TargetServer { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0)] get; }
[DispId(6)]
string ConnectedUser { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(6)] get; }
[DispId(7)]
string ConnectedDomain { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(7)] get; }
[DispId(8)]
uint HighestVersion { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(8)] get; }
}
[ComImport, DefaultMember("TargetServer"), Guid("0F87369F-A4E5-4CFC-BD3E-73E6154572DD"), ClassInterface((short)0), System.Security.SuppressUnmanagedCodeSecurity]
internal class TaskSchedulerClass
{
}
[ComImport, Guid("8FD4711D-2D02-4C8C-87E3-EFF699DE127E"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskSettings
{
bool AllowDemandStart { get; [param: In] set; }
string RestartInterval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
int RestartCount { get; [param: In] set; }
TaskInstancesPolicy MultipleInstances { get; [param: In] set; }
bool StopIfGoingOnBatteries { get; [param: In] set; }
bool DisallowStartIfOnBatteries { get; [param: In] set; }
bool AllowHardTerminate { get; [param: In] set; }
bool StartWhenAvailable { get; [param: In] set; }
string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
bool RunOnlyIfNetworkAvailable { get; [param: In] set; }
string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
bool Enabled { get; [param: In] set; }
string DeleteExpiredTaskAfter { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
int Priority { get; [param: In] set; }
TaskCompatibility Compatibility { get; [param: In] set; }
bool Hidden { get; [param: In] set; }
IIdleSettings IdleSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
bool RunOnlyIfIdle { get; [param: In] set; }
bool WakeToRun { get; [param: In] set; }
INetworkSettings NetworkSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
}
[ComImport, Guid("2C05C3F0-6EED-4c05-A15F-ED7D7A98A369"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskSettings2
{
bool DisallowStartOnRemoteAppSession { get; [param: In] set; }
bool UseUnifiedSchedulingEngine { get; [param: In] set; }
}
[ComImport, Guid("0AD9D0D7-0C7F-4EBB-9A5F-D1C648DCA528"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskSettings3 : ITaskSettings
{
new bool AllowDemandStart { get; [param: In] set; }
new string RestartInterval { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new int RestartCount { get; [param: In] set; }
new TaskInstancesPolicy MultipleInstances { get; [param: In] set; }
new bool StopIfGoingOnBatteries { get; [param: In] set; }
new bool DisallowStartIfOnBatteries { get; [param: In] set; }
new bool AllowHardTerminate { get; [param: In] set; }
new bool StartWhenAvailable { get; [param: In] set; }
new string XmlText { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool RunOnlyIfNetworkAvailable { get; [param: In] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
new string DeleteExpiredTaskAfter { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new int Priority { get; [param: In] set; }
new TaskCompatibility Compatibility { get; [param: In] set; }
new bool Hidden { get; [param: In] set; }
new IIdleSettings IdleSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new bool RunOnlyIfIdle { get; [param: In] set; }
new bool WakeToRun { get; [param: In] set; }
new INetworkSettings NetworkSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
bool DisallowStartOnRemoteAppSession { get; [param: In] set; }
bool UseUnifiedSchedulingEngine { get; [param: In] set; }
IMaintenanceSettings MaintenanceSettings { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
[return: MarshalAs(UnmanagedType.Interface)]
IMaintenanceSettings CreateMaintenanceSettings();
bool Volatile { get; [param: In] set; }
}
[ComImport, Guid("A6024FA8-9652-4ADB-A6BF-5CFCD877A7BA"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IMaintenanceSettings
{
string Period { [param: In, MarshalAs(UnmanagedType.BStr)] set; [return: MarshalAs(UnmanagedType.BStr)] get; }
string Deadline { [param: In, MarshalAs(UnmanagedType.BStr)] set; [return: MarshalAs(UnmanagedType.BStr)] get; }
bool Exclusive { [param: In] set; get; }
}
[ComImport, Guid("3E4C9351-D966-4B8B-BB87-CEBA68BB0107"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITaskVariables
{
[return: MarshalAs(UnmanagedType.BStr)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
string GetInput();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetOutput([In, MarshalAs(UnmanagedType.BStr)] string input);
[return: MarshalAs(UnmanagedType.BStr)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
string GetContext();
}
[ComImport, Guid("B45747E0-EBA7-4276-9F29-85C5BB300006"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITimeTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
[ComImport, Guid("09941815-EA89-4B5B-89E0-2A773801FAC3"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITrigger
{
TaskTriggerType Type { get; }
string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
bool Enabled { get; [param: In] set; }
}
[ComImport, Guid("85DF5081-1B24-4F32-878A-D9D14DF4CB77"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface ITriggerCollection
{
int Count { get; }
ITrigger this[int index] { [return: MarshalAs(UnmanagedType.Interface)] get; }
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
ITrigger Create([In] TaskTriggerType Type);
void Remove([In, MarshalAs(UnmanagedType.Struct)] object index);
void Clear();
}
[ComImport, Guid("5038FC98-82FF-436D-8728-A512A57C9DC1"), InterfaceType(ComInterfaceType.InterfaceIsDual), System.Security.SuppressUnmanagedCodeSecurity]
internal interface IWeeklyTrigger : ITrigger
{
new TaskTriggerType Type { get; }
new string Id { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new IRepetitionPattern Repetition { [return: MarshalAs(UnmanagedType.Interface)] get; [param: In, MarshalAs(UnmanagedType.Interface)] set; }
new string ExecutionTimeLimit { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string StartBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new string EndBoundary { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
new bool Enabled { get; [param: In] set; }
short DaysOfWeek { get; [param: In] set; }
short WeeksInterval { get; [param: In] set; }
string RandomDelay { [return: MarshalAs(UnmanagedType.BStr)] get; [param: In, MarshalAs(UnmanagedType.BStr)] set; }
}
}

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

@ -0,0 +1,37 @@
using System.Text.RegularExpressions;
using JetBrains.Annotations;
namespace Microsoft.Win32.TaskScheduler
{
/// <summary>
/// Represents a wildcard running on the
/// <see cref="System.Text.RegularExpressions"/> engine.
/// </summary>
public class Wildcard : Regex
{
/// <summary>
/// Initializes a wildcard with the given search pattern and options.
/// </summary>
/// <param name="pattern">The wildcard pattern to match.</param>
/// <param name="options">A combination of one or more <see cref="System.Text.RegularExpressions.RegexOptions"/>.</param>
public Wildcard([NotNull] string pattern, RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace)
: base(WildcardToRegex(pattern), options)
{
}
/// <summary>
/// Converts a wildcard to a regular expression.
/// </summary>
/// <param name="pattern">The wildcard pattern to convert.</param>
/// <returns>A regular expression equivalent of the given wildcard.</returns>
public static string WildcardToRegex([NotNull] string pattern)
{
string s = Regex.Escape(pattern);
s = Regex.Replace(s, @"(?<!\\)\\\*", @".*"); // Negative Lookbehind
s = Regex.Replace(s, @"\\\\\\\*", @"\*");
s = Regex.Replace(s, @"(?<!\\)\\\?", @"."); // Negative Lookbehind
s = Regex.Replace(s, @"\\\\\\\?", @"\?");
return string.Concat("^", Regex.Replace(s, @"\\\\\\\\", @"\\"), "$");
}
}
}

View File

@ -0,0 +1,526 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using JetBrains.Annotations;
namespace Microsoft.Win32.TaskScheduler
{
internal static class XmlSerializationHelper
{
public static object GetDefaultValue([NotNull] PropertyInfo prop)
{
var attributes = prop.GetCustomAttributes(typeof(DefaultValueAttribute), true);
if (attributes.Length > 0)
{
var defaultAttr = (DefaultValueAttribute)attributes[0];
return defaultAttr.Value;
}
// Attribute not found, fall back to default value for the type
if (prop.PropertyType.IsValueType)
return Activator.CreateInstance(prop.PropertyType);
return null;
}
private static bool GetPropertyValue(object obj, [NotNull] string property, ref object outVal)
{
PropertyInfo pi = obj?.GetType().GetProperty(property);
if (pi != null)
{
outVal = pi.GetValue(obj, null);
return true;
}
return false;
}
private static bool GetAttributeValue(Type objType, Type attrType, string property, bool inherit, ref object outVal)
{
object[] attrs = objType.GetCustomAttributes(attrType, inherit);
if (attrs.Length > 0)
return GetPropertyValue(attrs[0], property, ref outVal);
return false;
}
private static bool GetAttributeValue([NotNull] PropertyInfo propInfo, Type attrType, string property, bool inherit, ref object outVal)
{
Attribute attr = Attribute.GetCustomAttribute(propInfo, attrType, inherit);
return GetPropertyValue(attr, property, ref outVal);
}
private static bool IsStandardType(Type type) => type.IsPrimitive || type == typeof(DateTime) || type == typeof(DateTimeOffset) || type == typeof(Decimal) || type == typeof(Guid) || type == typeof(TimeSpan) || type == typeof(string) || type.IsEnum;
private static bool HasMembers([NotNull] object obj)
{
if (obj is IXmlSerializable)
{
using (System.IO.MemoryStream mem = new System.IO.MemoryStream())
{
using (XmlTextWriter tw = new XmlTextWriter(mem, Encoding.UTF8))
{
((IXmlSerializable)obj).WriteXml(tw);
tw.Flush();
return mem.Length > 3;
}
}
}
// Enumerate each public property
PropertyInfo[] props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var pi in props)
{
if (!Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false))
{
object value = pi.GetValue(obj, null);
if (!Equals(value, GetDefaultValue(pi)))
{
if (!IsStandardType(pi.PropertyType))
{
if (HasMembers(value))
return true;
}
else
return true;
}
}
}
return false;
}
public static string GetPropertyAttributeName([NotNull] PropertyInfo pi)
{
object oVal = null;
string eName = pi.Name;
if (GetAttributeValue(pi, typeof(XmlAttributeAttribute), "AttributeName", false, ref oVal))
eName = oVal.ToString();
return eName;
}
public static string GetPropertyElementName([NotNull] PropertyInfo pi)
{
object oVal = null;
string eName = pi.Name;
if (GetAttributeValue(pi, typeof(XmlElementAttribute), "ElementName", false, ref oVal))
eName = oVal.ToString();
else if (GetAttributeValue(pi.PropertyType, typeof(XmlRootAttribute), "ElementName", true, ref oVal))
eName = oVal.ToString();
return eName;
}
public delegate bool PropertyConversionHandler([NotNull] PropertyInfo pi, Object obj, ref Object value);
public static bool WriteProperty([NotNull] XmlWriter writer, [NotNull] PropertyInfo pi, [NotNull] Object obj, PropertyConversionHandler handler = null)
{
if (Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false) || Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false))
return false;
object value = pi.GetValue(obj, null);
object defValue = GetDefaultValue(pi);
if ((value == null && defValue == null) || (value != null && value.Equals(defValue)))
return false;
Type propType = pi.PropertyType;
if (handler != null && handler(pi, obj, ref value))
propType = value.GetType();
bool isStdType = IsStandardType(propType);
bool rw = pi.CanRead && pi.CanWrite;
bool ro = pi.CanRead && !pi.CanWrite;
string eName = GetPropertyElementName(pi);
if (isStdType && rw)
{
string output = GetXmlValue(value, propType);
if (output != null)
writer.WriteElementString(eName, output);
}
else if (!isStdType)
{
object outVal = null;
if (propType.GetInterface("IXmlSerializable") == null && GetAttributeValue(pi, typeof(XmlArrayAttribute), "ElementName", true, ref outVal) && propType.GetInterface("IEnumerable") != null)
{
if (string.IsNullOrEmpty(outVal.ToString())) outVal = eName;
writer.WriteStartElement(outVal.ToString());
var attributes = Attribute.GetCustomAttributes(pi, typeof(XmlArrayItemAttribute), true);
var dict = new Dictionary<Type, string>(attributes.Length);
foreach (XmlArrayItemAttribute a in attributes)
dict.Add(a.Type, a.ElementName);
foreach (object item in ((System.Collections.IEnumerable)value))
{
string aeName;
Type itemType = item.GetType();
if (dict.TryGetValue(itemType, out aeName))
{
if (IsStandardType(itemType))
writer.WriteElementString(aeName, GetXmlValue(item, itemType));
else
WriteObject(writer, item, null, false, aeName);
}
}
writer.WriteEndElement();
}
else
WriteObject(writer, value);
}
return false;
}
private static string GetXmlValue([NotNull] object value, Type propType)
{
string output = null;
if (propType.IsEnum)
{
if (Attribute.IsDefined(propType, typeof(FlagsAttribute), false))
output = Convert.ChangeType(value, Enum.GetUnderlyingType(propType)).ToString();
else
output = value.ToString();
}
else
{
switch (propType.FullName)
{
case "System.Boolean":
output = XmlConvert.ToString((System.Boolean)value);
break;
case "System.Byte":
output = XmlConvert.ToString((System.Byte)value);
break;
case "System.Char":
output = XmlConvert.ToString((System.Char)value);
break;
case "System.DateTime":
output = XmlConvert.ToString((System.DateTime)value, XmlDateTimeSerializationMode.RoundtripKind);
break;
case "System.DateTimeOffset":
output = XmlConvert.ToString((System.DateTimeOffset)value);
break;
case "System.Decimal":
output = XmlConvert.ToString((System.Decimal)value);
break;
case "System.Double":
output = XmlConvert.ToString((System.Double)value);
break;
case "System.Single":
output = XmlConvert.ToString((System.Single)value);
break;
case "System.Guid":
output = XmlConvert.ToString((System.Guid)value);
break;
case "System.Int16":
output = XmlConvert.ToString((System.Int16)value);
break;
case "System.Int32":
output = XmlConvert.ToString((System.Int32)value);
break;
case "System.Int64":
output = XmlConvert.ToString((System.Int64)value);
break;
case "System.SByte":
output = XmlConvert.ToString((System.SByte)value);
break;
case "System.TimeSpan":
output = XmlConvert.ToString((System.TimeSpan)value);
break;
case "System.UInt16":
output = XmlConvert.ToString((System.UInt16)value);
break;
case "System.UInt32":
output = XmlConvert.ToString((System.UInt32)value);
break;
case "System.UInt64":
output = XmlConvert.ToString((System.UInt64)value);
break;
default:
output = value == null ? string.Empty : value.ToString();
break;
}
}
return output;
}
public static void WriteObjectAttributes([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null)
{
// Enumerate each property
foreach (var pi in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
if (Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false))
WriteObjectAttribute(writer, pi, obj, handler);
}
public static void WriteObjectAttribute([NotNull] XmlWriter writer, [NotNull] PropertyInfo pi, [NotNull] object obj, PropertyConversionHandler handler = null)
{
object value = pi.GetValue(obj, null);
object defValue = GetDefaultValue(pi);
if ((value == null && defValue == null) || (value != null && value.Equals(defValue)))
return;
Type propType = pi.PropertyType;
if (handler != null && handler(pi, obj, ref value))
propType = value.GetType();
writer.WriteAttributeString(GetPropertyAttributeName(pi), GetXmlValue(value, propType));
}
public static void WriteObjectProperties([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null)
{
// Enumerate each public property
foreach (var pi in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
WriteProperty(writer, pi, obj, handler);
}
public static void WriteObject([NotNull] XmlWriter writer, [NotNull] object obj, PropertyConversionHandler handler = null, bool includeNS = false, string elemName = null)
{
if (obj == null)
return;
// Get name of top level element
string oName = elemName ?? GetElementName(obj);
if (!HasMembers(obj))
return;
if (includeNS)
writer.WriteStartElement(oName, GetTopLevelNamespace(obj));
else
writer.WriteStartElement(oName);
if (obj is IXmlSerializable)
{
((IXmlSerializable)obj).WriteXml(writer);
}
else
{
WriteObjectAttributes(writer, obj, handler);
WriteObjectProperties(writer, obj, handler);
}
writer.WriteEndElement();
}
public static string GetElementName([NotNull] object obj)
{
object oVal = null;
return GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "ElementName", true, ref oVal) ? oVal.ToString() : obj.GetType().Name;
}
public static string GetTopLevelNamespace([NotNull] object obj)
{
object oVal = null;
return GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "Namespace", true, ref oVal) ? oVal.ToString() : null;
}
public static void ReadObjectProperties([NotNull] XmlReader reader, [NotNull] object obj, PropertyConversionHandler handler = null)
{
// Build property lookup table
PropertyInfo[] props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
Dictionary<string, PropertyInfo> attrHash = new Dictionary<string, PropertyInfo>(props.Length);
Dictionary<string, PropertyInfo> propHash = new Dictionary<string, PropertyInfo>(props.Length);
foreach (var pi in props)
{
if (!Attribute.IsDefined(pi, typeof(XmlIgnoreAttribute), false))
{
if (Attribute.IsDefined(pi, typeof(XmlAttributeAttribute), false))
attrHash.Add(GetPropertyAttributeName(pi), pi);
else
propHash.Add(GetPropertyElementName(pi), pi);
}
}
if (reader.HasAttributes)
{
for (int i = 0; i < reader.AttributeCount; i++)
{
PropertyInfo pi;
reader.MoveToAttribute(i);
if (attrHash.TryGetValue(reader.LocalName, out pi))
{
if (IsStandardType(pi.PropertyType))
{
object value = null;
if (pi.PropertyType.IsEnum)
value = Enum.Parse(pi.PropertyType, reader.Value);
else
value = Convert.ChangeType(reader.Value, pi.PropertyType);
if (handler != null)
handler(pi, obj, ref value);
pi.SetValue(obj, value, null);
}
}
}
}
while (reader.MoveToContent() == XmlNodeType.Element)
{
PropertyInfo pi;
object outVal = null;
if (propHash.TryGetValue(reader.LocalName, out pi))
{
var tc = TypeDescriptor.GetConverter(pi.PropertyType);
if (IsStandardType(pi.PropertyType))
{
object value = null;
if (pi.PropertyType.IsEnum)
value = Enum.Parse(pi.PropertyType, reader.ReadElementContentAsString());
else if (pi.PropertyType == typeof(Guid))
value = new GuidConverter().ConvertFromString(reader.ReadElementContentAsString());
else
value = reader.ReadElementContentAs(pi.PropertyType, null);
if (handler != null)
handler(pi, obj, ref value);
pi.SetValue(obj, value, null);
}
else if (pi.PropertyType == typeof(Version))
{
Version v = new Version(reader.ReadElementContentAsString());
pi.SetValue(obj, v, null);
}
else if (pi.PropertyType.GetInterface("IEnumerable") != null && pi.PropertyType.GetInterface("IXmlSerializable") == null && GetAttributeValue(pi, typeof(XmlArrayAttribute), "ElementName", true, ref outVal))
{
string elem = string.IsNullOrEmpty(outVal?.ToString()) ? pi.Name : outVal.ToString();
reader.ReadStartElement(elem);
var attributes = Attribute.GetCustomAttributes(pi, typeof(XmlArrayItemAttribute), true);
var dict = new Dictionary<string, Type>(attributes.Length);
foreach (XmlArrayItemAttribute a in attributes)
dict.Add(a.ElementName, a.Type);
List<object> output = new List<object>();
while (reader.MoveToContent() == XmlNodeType.Element)
{
Type itemType;
if (dict.TryGetValue(reader.LocalName, out itemType))
{
object o;
if (IsStandardType(itemType))
o = reader.ReadElementContentAs(itemType, null);
else
{
o = Activator.CreateInstance(itemType);
ReadObject(reader, o, handler);
}
if (o != null)
output.Add(o);
}
}
reader.ReadEndElement();
if (output.Count > 0)
{
System.Collections.IEnumerable par = output;
Type et = typeof(object);
if (dict.Count == 1)
{
foreach (var v in dict.Values) { et = v; break; }
}
/*else
{
Type t1 = output[0].GetType();
bool same = true;
foreach (var item in output)
if (item.GetType() != t1) { same = false; break; }
if (same)
et = t1;
}
if (et != typeof(object))
{
Array ao = Array.CreateInstance(et, output.Count);
for (int i = 0; i < output.Count; i++)
ao.SetValue(output[i], i);
par = ao;
}
else
par = output.ToArray();*/
bool done = false;
if (pi.PropertyType == par.GetType() || (pi.PropertyType.IsArray && (pi.PropertyType.GetElementType() == typeof(object) || pi.PropertyType.GetElementType() == et)))
try { pi.SetValue(obj, par, null); done = true; } catch { }
if (!done)
{
var mi = pi.PropertyType.GetMethod("AddRange", new Type[] { typeof(System.Collections.IEnumerable) });
if (mi != null)
try { mi.Invoke(pi.GetValue(obj, null), new object[] { par }); done = true; } catch { }
}
if (!done)
{
var mi = pi.PropertyType.GetMethod("Add", new Type[] { typeof(object) });
if (mi != null)
try { foreach (var i in par) mi.Invoke(pi.GetValue(obj, null), new object[] { i }); done = true; } catch { }
}
if (!done && et != typeof(Object))
{
var mi = pi.PropertyType.GetMethod("Add", new Type[] { et });
if (mi != null)
try { foreach (var i in par) mi.Invoke(pi.GetValue(obj, null), new object[] { i }); done = true; } catch { }
}
// Throw error if not done
}
}
else
{
object inst = pi.GetValue(obj, null) ?? Activator.CreateInstance(pi.PropertyType);
if (inst == null)
throw new InvalidOperationException($"Can't get instance of {pi.PropertyType.Name}.");
ReadObject(reader, inst, handler);
}
}
else
{
reader.Skip();
reader.MoveToContent();
}
}
}
public static void ReadObject([NotNull] XmlReader reader, [NotNull] object obj, PropertyConversionHandler handler = null)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
reader.MoveToContent();
if (obj is IXmlSerializable)
{
((IXmlSerializable)obj).ReadXml(reader);
}
else
{
object oVal = null;
string oName = GetAttributeValue(obj.GetType(), typeof(XmlRootAttribute), "ElementName", true, ref oVal) ? oVal.ToString() : obj.GetType().Name;
if (reader.LocalName != oName)
throw new XmlException("XML element name does not match object.");
if (!reader.IsEmptyElement)
{
reader.ReadStartElement();
reader.MoveToContent();
ReadObjectProperties(reader, obj, handler);
reader.ReadEndElement();
}
else
reader.Skip();
}
}
public static void ReadObjectFromXmlText([NotNull] string xml, [NotNull] object obj, PropertyConversionHandler handler = null)
{
using (System.IO.StringReader sr = new System.IO.StringReader(xml))
{
using (XmlReader reader = XmlReader.Create(sr))
{
reader.MoveToContent();
ReadObject(reader, obj, handler);
}
}
}
public static string WriteObjectToXmlText([NotNull] object obj, PropertyConversionHandler handler = null)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
WriteObject(writer, obj, handler, true);
return sb.ToString();
}
}
}

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup>
</configuration>

View File

@ -1,4 +0,0 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]

View File

@ -1,4 +0,0 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]

View File

@ -1 +0,0 @@
26d71f5f180327a37d2c65ba8cf4401c2e6c1582

View File

@ -1,9 +0,0 @@
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\obj\Release\FlashPatcher.FlashPatcherForm.resources
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\obj\Release\FlashPatcher.Properties.Resources.resources
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\obj\Release\FlashPatcher.csproj.GenerateResource.cache
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\obj\Release\FlashPatcher.csproj.CoreCompileInputs.cache
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\obj\Release\Flash Patcher.exe
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\obj\Release\Flash Patcher.pdb
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\bin\Release\Flash Patcher.exe.config
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\bin\Release\Flash Patcher.exe
C:\Users\User\Documents\FlashPatcher-1.6\FlashPatcher\bin\Release\Flash Patcher.pdb