using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Microsoft.Win32.TaskScheduler
{
///
/// Split-panel that displays a list of all events associated with a task with a hidable detail pane.
///
[System.Drawing.ToolboxBitmap(typeof(Microsoft.Win32.TaskScheduler.TaskEditDialog), "TaskControl")]
public partial class TaskHistoryControl : UserControl
{
private long historyEventCount = 0;
private ListViewColumnSorter lvwColumnSorter = new ListViewColumnSorter();
private Task task;
private int selectedIndex = -1;
private IList vcache = new System.Collections.Generic.SparseArray();
private TaskEventEnumerator vevEnum;
private TaskEventLog vlog;
///
/// Initializes a new instance of the class.
///
public TaskHistoryControl()
{
InitializeComponent();
this.ShowErrors = true;
historyListView.ListViewItemSorter = lvwColumnSorter;
historyListView.VirtualMode = true;
historyListView.ColumnContextMenuStrip = columnContextMenu;
if (historyListImages.Images.Count == 0)
{
historyListImages.Images.Add(EditorProperties.Resources.empty, Color.FromArgb(0xff, 0, 0xff));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.critical, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.error, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.warning, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.info, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.verbose, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.auditSuccess, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.auditFail, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.filter, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.refresh, 0x10, 0x10));
historyListImages.Images.Add(new Icon(EditorProperties.Resources.end, 0x10, 0x10));
historyFilterIcon.ImageIndex = 8;
historyClearBtn.ImageIndex = 9;
refreshBtn.ImageIndex = 9;
}
}
///
/// Gets or sets a value indicating whether errors are shown in the UI.
///
/// true if errors are shown; otherwise, false.
[DefaultValue(true), Category("Behavior"), Description("Determines whether errors are shown in the UI.")]
public bool ShowErrors { get; set; }
///
/// Gets or sets the task used to retrieve the history for this control.
///
///
/// The task whose history is displayed.
///
[DefaultValue(null), Browsable(false)]
public Task Task
{
get { return this.task; }
set
{
this.task = value;
historyDetailView.ActiveTab = EventViewerControl.EventViewerActiveTab.General;
historySplitContainer.Panel2Collapsed = false;
if (value != null)
vlog = CreateLogInstance();
RefreshHistory();
}
}
///
/// Activates this instance. Call when the control receives initial focus or needs to refresh.
///
/// The for which to get the history.
[Obsolete("The Activate method is being deprecated. Use the Task property instead.")]
public void Activate(Task t)
{
this.Task = t;
}
///
/// Refreshes the history list of the control using current sorting and grouping settings.
///
public void RefreshHistory()
{
historyListView.Cursor = Cursors.WaitCursor;
historyListView.VirtualListSize = 0;
historyListView.Refresh();
historyHeader_Refresh(-1);
historyDetailView.TaskEvent = null;
selectedIndex = -1;
vcache.Clear();
historyBackgroundWorker.RunWorkerAsync();
}
///
/// Raises the event.
///
/// An that contains the event data.
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// TODO: Read last status from user options
// Get column names and add them to the context menu
var c = historyListView.Columns;
int insIdx = columnContextMenu.Items.IndexOf(columnContextMenuBreak);
for (int i = 0; i < c.Count; i++)
{
var tsi = new ToolStripMenuItem(c[i].Text, null, columnContextMenuColumnHeaderItem_onClick) { Checked = c[i].Width > 0, Tag = c[i].Index };
columnContextMenu.Items.Insert(++insIdx, tsi);
}
}
private ListViewItem BuildItem(TaskEvent item)
{
var kwds = new List(item.EventRecord.KeywordsDisplayNames);
return new ListViewItem(new string[] { item.Level, item.TimeCreated.ToString(), item.EventId.ToString(),
item.TaskCategory, item.OpCode, item.ActivityId.ToString(), string.Join(", ", kwds.ToArray()), "TaskScheduler",
item.UserId.Translate(typeof(System.Security.Principal.NTAccount)).ToString(), item.EventRecord.LogName,
item.EventRecord.MachineName, item.ProcessId.ToString(), item.EventRecord.ThreadId.ToString() },
item.EventRecord.Level.GetValueOrDefault(0)) { Tag = item };
}
private void columnContextMenuColumnHeaderItem_onClick(object sender, EventArgs e)
{
ToolStripMenuItem item = (ToolStripMenuItem)sender;
int cIndex = (int)item.Tag;
item.Checked = !item.Checked;
if (item.Checked)
{
historyListView.Columns[cIndex].Width = 100; // TODO: Be more acurate here and get a good standard width
}
else
historyListView.Columns[cIndex].Width = 0;
PersistColumnSettings();
}
private TaskEventLog CreateLogInstance()
{
return new TaskEventLog(task.TaskService.TargetServer, task.Path);
}
private void FetchEnumEvents(int startIndex, int endIndex)
{
int n = startIndex;
vevEnum.Seek(System.IO.SeekOrigin.Begin, n);
while (vevEnum.MoveNext() && n <= endIndex)
vcache[n++] = BuildItem(vevEnum.Current);
}
private void histDetailHideBtn_Click(object sender, EventArgs e)
{
historySplitContainer.Panel2Collapsed = true;
}
private void historyBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
TaskEventLog log = CreateLogInstance();
if (!lvwColumnSorter.Group && lvwColumnSorter.SortColumn == 1)
{
e.Result = null;
}
else
{
List eList = new List(log);
List list = eList.ConvertAll(delegate(TaskEvent te) { return BuildItem(te); });
list.Sort(lvwColumnSorter);
e.Result = list;
}
((BackgroundWorker)sender).ReportProgress(100, log.Count);
}
catch (Exception ex) { e.Result = ex; }
}
private void historyBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState is long)
{
historyListView.VirtualListSize = (int)Math.Min(Int32.MaxValue, (long)e.UserState);
historyHeader_Refresh((long)e.UserState);
}
}
private void historyBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
historyListView.Cursor = Cursors.Default;
historyListView.BeginUpdate();
if (e.Result is Exception)
{
historyHeader_Refresh(0L);
if (ShowErrors)
MessageBox.Show(this, string.Format(EditorProperties.Resources.Error_CannotRetrieveHistory, ((Exception)e.Result).Message), EditorProperties.Resources.TaskSchedulerName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (e.Result == null)
{
historyListView.Items.Clear();
historyListView.VirtualMode = true;
vcache = new System.Collections.Generic.SparseArray();
vevEnum = vlog.GetEnumerator(lvwColumnSorter.Order == SortOrder.Ascending) as TaskEventEnumerator;
}
else
{
historyListView.VirtualMode = false;
vevEnum = null;
vcache = (IList)e.Result;
historyListView.Items.AddRange(((List)e.Result).ToArray());
if (lvwColumnSorter.Group)
SetupGroups();
else
historyListView.ShowGroups = false;
}
historyListView.EndUpdate();
historyListView.Focus();
}
private void SetupGroups()
{
historyListView.Groups.Clear();
int col = lvwColumnSorter.SortColumn;
ListViewGroupEx g = new ListViewGroupEx();
for (int i = 0; i < historyListView.Items.Count; i++)
{
var lvi = historyListView.Items[i];
string cText = lvi.SubItems[col].Text;
if (cText != g.Header)
{
g = historyListView.Groups.Add(cText);
g.Collapsible = true;
g.Collapsed = false;
}
g.Items.Add(lvi);
}
historyListView.ShowGroups = true;
}
private void historyHeader_Refresh(long cnt)
{
if (historyEventCount != cnt)
{
historyEventCount = cnt;
historyHeader.Text = cnt == -1 ? EditorProperties.Resources.LoadingPrompt : string.Format(EditorProperties.Resources.HistoryHeader, cnt);
}
}
private void historyListView_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e)
{
if (vcache[e.StartIndex] == null && vevEnum != null)
{
FetchEnumEvents(e.StartIndex, e.EndIndex);
}
}
private void historyListView_ColumnClick(object sender, ColumnClickEventArgs e)
{
lvwColumnSorter.ResortOnColumn(e.Column);
historyListView.SetSortIcon(lvwColumnSorter.SortColumn, lvwColumnSorter.Order);
RefreshHistory();
}
private void historyListView_ColumnReordered(object sender, ColumnReorderedEventArgs e)
{
PersistColumnSettings();
}
private void historyListView_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
{
PersistColumnSettings();
}
private void historyListView_DoubleClick(object sender, EventArgs e)
{
if (selectedIndex != -1)
{
ListViewItem lvi = vcache[selectedIndex];
if (lvi != null)
{
EventViewerDialog dlg = new EventViewerDialog();
dlg.Initialize(lvi.Tag as TaskEvent, null); //TaskService == null ? new TaskEventLog(task.Path) : new TaskEventLog(TaskService.TargetServer, task.Path));
dlg.ShowDialog(this);
}
}
}
private void historyListView_MouseClick(object sender, MouseEventArgs e)
{
var lvi = historyListView.GetItemAt(e.X, e.Y);
if (lvi != null && e.Button == System.Windows.Forms.MouseButtons.Right)
{
listContextMenu.Show(historyListView, e.Location);
}
}
private void historyListView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
ListViewItem item = (e.ItemIndex >= 0 && e.ItemIndex < vcache.Count) ? vcache[e.ItemIndex] : null;
//System.Diagnostics.Debug.WriteLine(string.Format("RetrieveLVI: InCache={0}, Msg={1}", item!=null, Environment.StackTrace));
if (item == null && vevEnum != null)
{
FetchEnumEvents(e.ItemIndex, e.ItemIndex);
item = vcache[e.ItemIndex];
}
if (item != null)
{
e.Item = item;
if (historyListView.SelectedIndices.Count == 0)
historyListView.SelectedIndices.Add(e.ItemIndex);
}
}
private void historyListView_SelectedIndexChanged(object sender, EventArgs e)
{
if (historyListView.SelectedIndices.Count > 0)
{
int newSelIdx = historyListView.SelectedIndices[0];
SelectItemChanged(newSelIdx);
}
else
{
selectedIndex = -1;
historyDetailView.TaskEvent = null;
historyDetailTitleText.Text = string.Empty;
}
}
private void PersistColumnSettings()
{
// TODO: Figure out how to persist column settings
}
private void SelectItemChanged(int newSelIdx)
{
if (selectedIndex != newSelIdx)
{
selectedIndex = newSelIdx;
ListViewItem lvi = vcache[selectedIndex];
if (lvi != null)
{
TaskEvent ev = lvi.Tag as TaskEvent;
historyDetailView.TaskEvent = ev;
historyDetailTitleText.Text = ev == null ? string.Empty : string.Format(EditorProperties.Resources.EventDetailHeader, ev.EventId);
}
}
}
private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
{
this.RefreshHistory();
}
private void eventPropertiesToolStripMenuItem_Click(object sender, EventArgs e)
{
historyListView_DoubleClick(null, EventArgs.Empty);
}
private void attachTaskToThisEventToolStripMenuItem_Click(object sender, EventArgs e)
{
if (selectedIndex != -1)
{
var taskEvent = (TaskEvent)vcache[selectedIndex].Tag;
if (taskEvent != null)
{
var td = task.TaskService.NewTask();
var eventId = taskEvent.EventId;
td.Triggers.Add(new EventTrigger("Microsoft-Windows-TaskScheduler/Operational", "TaskScheduler", eventId));
td.Actions.Add(new ExecAction());
using (var wiz = new TaskSchedulerWizard(task.TaskService, td, true))
{
wiz.AvailableTriggers = TaskSchedulerWizard.AvailableWizardTriggers.Event;
wiz.AvailableActions = TaskSchedulerWizard.AvailableWizardActions.Execute;
wiz.AvailablePages = TaskSchedulerWizard.AvailableWizardPages.IntroPage | TaskSchedulerWizard.AvailableWizardPages.SecurityPage | TaskSchedulerWizard.AvailableWizardPages.SummaryPage | TaskSchedulerWizard.AvailableWizardPages.TriggerEditPage | TaskSchedulerWizard.AvailableWizardPages.ActionEditPage;
wiz.TaskName = string.Format("Microsoft-Windows-TaskScheduler_Operational_Microsoft-Windows-TaskScheduler_{0}", eventId);
wiz.ShowDialog(this);
}
}
}
}
private void saveAllEventsAsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
// TODO: Save event list
}
}
private static Converter cdel = delegate(ColumnHeader c) { return c.Text; };
private static List GetColumnHeaderList(ListView.ColumnHeaderCollection col)
{
var l = new List(col.Count);
for (int i = 0; i < col.Count; i++)
l.Add(col[i]);
return l;
}
private static List GetColumnHeaderTextList(ListView.ColumnHeaderCollection col)
{
return GetColumnHeaderList(col).ConvertAll(cdel);
}
private void addremoveColumnsToolStripMenuItem_Click(object sender, EventArgs e)
{
var cols = GetColumnHeaderList(historyListView.Columns);
var allHeaders = cols.ConvertAll(cdel).ToArray();
var shownHeaders = cols.FindAll(delegate(ColumnHeader c) { return c.Width > 0; });
shownHeaders.Sort(delegate(ColumnHeader a, ColumnHeader b) { return a.DisplayIndex.CompareTo(b.DisplayIndex); });
using (ListColumnEditor colEd = new ListColumnEditor(allHeaders, allHeaders, shownHeaders.ConvertAll(cdel).ToArray()))
{
if (colEd.ShowDialog(this) == DialogResult.OK)
{
// TODO: Reorder columns
}
}
}
private void sortEventsByThisColumnToolStripMenuItem_Click(object sender, EventArgs e)
{
int col = (int)this.columnContextMenu.Tag;
historyListView_ColumnClick(historyListView, new ColumnClickEventArgs(col));
removeSortingToolStripMenuItem.Visible = true;
}
private void removeSortingToolStripMenuItem_Click(object sender, EventArgs e)
{
historyListView_ColumnClick(historyListView, new ColumnClickEventArgs(1));
removeSortingToolStripMenuItem.Visible = false;
}
private void groupEventsByThisColumnToolStripMenuItem_Click(object sender, EventArgs e)
{
int col = (int)this.columnContextMenu.Tag;
lvwColumnSorter.Group = true;
if (lvwColumnSorter.SortColumn != col || vevEnum != null)
historyListView_ColumnClick(historyListView, new ColumnClickEventArgs(col));
else
SetupGroups();
groupEventsByThisColumnToolStripMenuItem.Visible = false;
removeGroupingOfEventsToolStripMenuItem.Visible = expandAllGroupsToolStripMenuItem.Visible = collapseAllGroupsToolStripMenuItem.Visible = true;
}
private void removeGroupingOfEventsToolStripMenuItem_Click(object sender, EventArgs e)
{
lvwColumnSorter.Group = false;
groupEventsByThisColumnToolStripMenuItem.Visible = true;
removeGroupingOfEventsToolStripMenuItem.Visible = expandAllGroupsToolStripMenuItem.Visible = collapseAllGroupsToolStripMenuItem.Visible = false;
}
private void expandAllGroupsToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (var item in historyListView.Groups)
item.Collapsed = false;
}
private void collapseAllGroupsToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (var item in historyListView.Groups)
item.Collapsed = true;
}
internal class ListViewColumnSorter : IComparer, System.Collections.IComparer
{
private System.Collections.CaseInsensitiveComparer ObjectCompare = new System.Collections.CaseInsensitiveComparer(System.Globalization.CultureInfo.InvariantCulture);
public ListViewColumnSorter()
{
Group = false;
NewSortSameColumn = false;
Order = SortOrder.Descending;
SortColumn = 1;
}
public int Compare(ListViewItem listviewX, ListViewItem listviewY)
{
// Compare the two items
int compareResult = ObjectCompare.Compare(listviewX.SubItems[SortColumn].Text, listviewY.SubItems[SortColumn].Text);
// Calculate correct return value based on object comparison
if (Order == SortOrder.Ascending)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
else if (Order == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
}
// Return '0' to indicate they are equal
return 0;
}
public void ResortOnColumn(int column)
{
if (column == this.SortColumn)
{
// Reverse the current sort direction for this column.
this.Order = this.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
this.NewSortSameColumn = true;
}
else
{
// Set the column number that is to be sorted; default to ascending.
this.SortColumn = column;
this.Order = SortOrder.Ascending;
this.NewSortSameColumn = false;
}
}
int System.Collections.IComparer.Compare(object x, object y)
{
if (x is ListViewItem && y is ListViewItem)
return Compare((ListViewItem)x, (ListViewItem)y);
return ObjectCompare.Compare(x, y);
}
public bool NewSortSameColumn { get; set; }
public SortOrder Order { get; set; }
public int SortColumn { get; set; }
public bool Group { get; set; }
}
}
}