From d1015eddb279da28cfdda5aa5acec43f8719bdf2 Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Tue, 14 Jun 2016 17:25:35 -0700 Subject: [PATCH 1/8] Add project.json to src/Microsoft.PowerShell.Commands.Diagnostics/project.json --- .../project.json | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/project.json diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/project.json b/src/Microsoft.PowerShell.Commands.Diagnostics/project.json new file mode 100644 index 000000000..ccd238c41 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/project.json @@ -0,0 +1,35 @@ +{ + "name": "Microsoft.PowerShell.Commands.Diagnostics", + "version": "1.0.0-*", + "authors": [ "garretts", "andschwa" ], + + "compilationOptions": { + "warningsAsErrors": true, + "allowUnsafe": true + }, + + "dependencies": { + "System.Management.Automation": "1.0.0-*" + }, + + "frameworks": { + "netstandard1.5": { + "imports": [ "dnxcore50" ], + "compilationOptions": { + "define": [ "CORECLR" ] + }, + "buildOptions": { + "compile": { + "excludeFiles": [ "GetEventCommand.cs","PdhSafeHandle.cs","GetEventSnapin.cs","CounterSample.cs","CounterSet.cs","PdhHelper.cs" ,"ExportCounterCommand.cs","GetCounterCommand.cs","ImportCounterCommand.cs","NewWinEventCommand.cs"] + } + } + }, + "net451": { + "frameworkAssemblies": { + "System": "" + } + } + }, + + "compile": [ "../windows-build/gen/Microsoft.PowerShell.Commands.Diagnostics/*.cs" ] +} From 29271ec2d770121e4b24a01538a20bb91c3d2cf8 Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Tue, 14 Jun 2016 17:27:19 -0700 Subject: [PATCH 2/8] Add map.json to src/Microsoft.PowerShell.Commands.Diagnostics --- .../map.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/map.json diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/map.json b/src/Microsoft.PowerShell.Commands.Diagnostics/map.json new file mode 100644 index 000000000..17d19fdbe --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/map.json @@ -0,0 +1,14 @@ +{ + "wmi/Events/GetEvent/src/CommonUtils.cs": "CommonUtils.cs", + "wmi/Events/GetEvent/src/CounterFileInfo.cs": "CounterFileInfo.cs", + "wmi/Events/GetEvent/src/CounterSample.cs": "CounterSample.cs", + "wmi/Events/GetEvent/src/CounterSet.cs": "CounterSet.cs", + "wmi/Events/GetEvent/src/ExportCounterCommand.cs": "ExportCounterCommand.cs", + "wmi/Events/GetEvent/src/GetCounterCommand.cs": "GetCounterCommand.cs", + "wmi/Events/GetEvent/src/GetEventCommand.cs": "GetEventCommand.cs", + "wmi/Events/GetEvent/src/GetEventSnapin.cs": "GetEventSnapin.cs", + "wmi/Events/GetEvent/src/ImportCounterCommand.cs": "ImportCounterCommand.cs", + "wmi/Events/GetEvent/src/NewWinEventCommand.cs": "NewWinEventCommand.cs", + "wmi/Events/GetEvent/src/PdhHelper.cs": "PdhHelper.cs", + "wmi/Events/GetEvent/src/PdhSafeHandle.cs": "PdhSafeHandle.cs" +} From 61ea879f735b4f0d68e078ee6edf5c31ab8e7b3e Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Tue, 14 Jun 2016 17:41:39 -0700 Subject: [PATCH 3/8] Add AssemblyInfo for Diagnostics --- .../AssemblyInfo.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs new file mode 100644 index 000000000..5ef0bbe75 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs @@ -0,0 +1,13 @@ +using System.Reflection; +using System.Resources; + +[assembly:AssemblyFileVersionAttribute("3.0.0.0")] +[assembly:AssemblyVersion("3.0.0.0")] + +[assembly:AssemblyCulture("")] +[assembly:NeutralResourcesLanguage("en-US")] + +#if !CORECLR +[assembly:AssemblyKeyFileAttribute(@"..\signing\visualstudiopublic.snk")] +[assembly:AssemblyDelaySignAttribute(true)] +#endif From 4e4a07c5e8141f229689048be63edd1965460ef1 Mon Sep 17 00:00:00 2001 From: PowerShell Team Date: Tue, 14 Jun 2016 17:43:34 -0700 Subject: [PATCH 4/8] Copy files for Microsoft.PowerShell.Commands.Diagnostics [SD:714215] Commit 2e2daeaff52930d981a31e0030d7161c4b45cc48 --- .../CommonUtils.cs | 122 + .../CounterFileInfo.cs | 60 + .../CounterSample.cs | 180 ++ .../CounterSet.cs | 135 ++ .../ExportCounterCommand.cs | 376 +++ .../GetCounterCommand.cs | 645 +++++ .../GetEventCommand.cs | 2101 +++++++++++++++++ .../GetEventSnapin.cs | 111 + .../ImportCounterCommand.cs | 676 ++++++ .../NewWinEventCommand.cs | 352 +++ .../PdhHelper.cs | 1796 ++++++++++++++ .../PdhSafeHandle.cs | 77 + 12 files changed, 6631 insertions(+) create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs new file mode 100644 index 000000000..cd2050f3d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + + +namespace Microsoft.PowerShell.Commands.Diagnostics.Common +{ + + internal class CommonUtilities + { + // + // No-op private ctor to prevent the default ctor from getting generated. + // This class is intended to only have static members. + // + private CommonUtilities() { } + + // + // StringArrayToString helper converts a string array into a comma-separated string. + // Note this has only limited use, individual strings cannot have commas. + // + public static string StringArrayToString (IEnumerable input) + { + string ret = ""; + foreach (string element in input) + { + ret += element + ", "; + } + + ret = ret.TrimEnd(); + ret = ret.TrimEnd(','); + + return ret; + } + + + const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; + const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; + const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; + const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; + const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, + uint dwMessageId, uint dwLanguageId, + [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder lpBuffer, + uint nSize, IntPtr Arguments); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibraryEx( + [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, + IntPtr hFile, + uint dwFlags + ); + + [DllImport("kernel32.dll")] + private static extern bool FreeLibrary(IntPtr hModule); + + + [DllImport("kernel32.dll", EntryPoint = "GetUserDefaultLangID", CallingConvention = CallingConvention.Winapi, SetLastError = true)] + private static extern ushort GetUserDefaultLangID(); + + + public static uint FormatMessageFromModule(uint lastError, string moduleName, out String msg) + { + Debug.Assert (!string.IsNullOrEmpty(moduleName)); + + uint formatError = 0; + msg = String.Empty; + IntPtr moduleHandle = IntPtr.Zero; + + moduleHandle = LoadLibraryEx(moduleName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); + if (moduleHandle == IntPtr.Zero) + { + return (uint)Marshal.GetLastWin32Error(); + } + + try + { + uint dwFormatFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE; + uint LANGID = (uint)GetUserDefaultLangID(); + uint langError = (uint)Marshal.GetLastWin32Error(); + if (langError != 0) + { + LANGID = 0; // neutral + } + + StringBuilder outStringBuilder = new StringBuilder(1024); + uint nChars = FormatMessage(dwFormatFlags, + moduleHandle, + lastError, + LANGID, + outStringBuilder, + (uint)outStringBuilder.Capacity, + IntPtr.Zero); + + if (nChars == 0) + { + formatError = (uint)Marshal.GetLastWin32Error(); + //Console.WriteLine("Win32FormatMessage", String.Format(null, "Error formatting message: {0}", formatError)); + } + else + { + msg = outStringBuilder.ToString(); + if (msg.EndsWith(Environment.NewLine, StringComparison.Ordinal)) + { + msg = msg.Substring(0, msg.Length - 2); + } + } + } + finally + { + FreeLibrary(moduleHandle); + } + return formatError; + } + + } +} + diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs new file mode 100644 index 000000000..c487309f3 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2008 Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Diagnostics; +using System.ComponentModel; + +namespace Microsoft.PowerShell.Commands.GetCounter +{ + + + public class CounterFileInfo + { + internal CounterFileInfo(DateTime oldestRecord, + DateTime newestRecord, + UInt32 sampleCount) + { + + _oldestRecord = oldestRecord; + _newestRecord = newestRecord; + _sampleCount = sampleCount; + } + + internal CounterFileInfo(){} + + public DateTime OldestRecord + { + get + { + return _oldestRecord; + } + } + private DateTime _oldestRecord = DateTime.MinValue; + + public DateTime NewestRecord + { + get + { + return _newestRecord; + } + } + private DateTime _newestRecord = DateTime.MaxValue; + + public UInt32 SampleCount + { + get + { + return _sampleCount; + } + } + private UInt32 _sampleCount = 0; + + } + +} + diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs new file mode 100644 index 000000000..29bdd7845 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs @@ -0,0 +1,180 @@ +// +// Copyright (c) 2008 Microsoft Corporation. All rights reserved. +// + + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; +using System.ComponentModel; +using System.Resources; +using Microsoft.Powershell.Commands.GetCounter.PdhNative; +using System.Globalization; +using System.Diagnostics.CodeAnalysis; + + +namespace Microsoft.PowerShell.Commands.GetCounter +{ + + public class PerformanceCounterSample + { + internal PerformanceCounterSample() + { + } + + internal PerformanceCounterSample(string path, + string instanceName, + double cookedValue, + UInt64 rawValue, + UInt64 secondValue, + uint multiCount, + PerformanceCounterType counterType, + UInt32 defaultScale, + UInt64 timeBase, + DateTime timeStamp, + UInt64 timeStamp100nSec, + UInt32 status) + { + _path = path; + _instanceName = instanceName; + _cookedValue = cookedValue; + _rawValue = rawValue; + _secondValue = secondValue; + _multiCount = multiCount; + _counterType = counterType; + _defaultScale = defaultScale; + _timeBase = timeBase; + _timeStamp = timeStamp; + _timeStamp100nSec = timeStamp100nSec; + _status = status; + } + + public string Path + { + get {return _path;} + set {_path = value;} + } + private string _path = ""; + + + public string InstanceName + { + get {return _instanceName;} + set {_instanceName = value;} + } + private string _instanceName = ""; + + + public double CookedValue + { + get {return _cookedValue;} + set {_cookedValue = value;} + } + private double _cookedValue = 0; + + public UInt64 RawValue + { + get {return _rawValue;} + set {_rawValue = value;} + } + private UInt64 _rawValue = 0; + + public UInt64 SecondValue + { + get {return _secondValue;} + set {_secondValue = value;} + } + private UInt64 _secondValue = 0; + + public uint MultipleCount + { + get {return _multiCount;} + set {_multiCount = value;} + } + private uint _multiCount = 0; + + public PerformanceCounterType CounterType + { + get {return _counterType;} + set {_counterType = value;} + } + private PerformanceCounterType _counterType = 0; + + public DateTime Timestamp + { + get {return _timeStamp;} + set {_timeStamp = value;} + } + private DateTime _timeStamp = DateTime.MinValue; + + + public UInt64 Timestamp100NSec + { + get {return _timeStamp100nSec;} + set {_timeStamp100nSec = value;} + } + private UInt64 _timeStamp100nSec = 0; + + public UInt32 Status + { + get {return _status;} + set {_status = value;} + } + private UInt32 _status = 0; + + public UInt32 DefaultScale + { + get {return _defaultScale;} + set {_defaultScale = value;} + } + private UInt32 _defaultScale = 0; + + public UInt64 TimeBase + { + get {return _timeBase;} + set {_timeBase = value;} + } + private UInt64 _timeBase = 0; + + + + } + + public class PerformanceCounterSampleSet + { + internal PerformanceCounterSampleSet() + { + _resourceMgr = new ResourceManager("GetEventResources", Assembly.GetExecutingAssembly()); + } + + internal PerformanceCounterSampleSet (DateTime timeStamp, + PerformanceCounterSample[] counterSamples, + bool firstSet) : this() + { + _timeStamp = timeStamp; + _counterSamples = counterSamples; + } + + public DateTime Timestamp + { + get {return _timeStamp;} + set {_timeStamp = value;} + } + private DateTime _timeStamp = DateTime.MinValue; + + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSample.CounterSamples", + Justification="A string[] is required here because that is the type Powershell supports")] + public PerformanceCounterSample[] CounterSamples + { + get {return _counterSamples;} + set {_counterSamples = value;} + } + private PerformanceCounterSample[] _counterSamples = null; + + private ResourceManager _resourceMgr = null; + + } +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs new file mode 100644 index 000000000..76b1c397f --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs @@ -0,0 +1,135 @@ +// +// Copyright (c) 2008 Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Diagnostics; +using System.ComponentModel; + +namespace Microsoft.PowerShell.Commands.GetCounter +{ + + public class CounterSet + { + internal CounterSet(string setName, + string machineName, + PerformanceCounterCategoryType categoryType, + string setHelp, + ref Dictionary counterInstanceMapping) + { + _counterSetName = setName; + if (machineName == null || machineName.Length == 0) + { + machineName = "."; + } + else + { + _machineName = machineName; + if (!_machineName.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)) + { + _machineName = @"\\" + _machineName; + } + } + _counterSetType = categoryType; + _description = setHelp; + _counterInstanceMapping = counterInstanceMapping; + } + + public string CounterSetName + { + get + { + return _counterSetName; + } + } + private string _counterSetName = ""; + + public string MachineName + { + get + { + return _machineName; + } + } + private string _machineName = "."; + + public PerformanceCounterCategoryType CounterSetType + { + get + { + return _counterSetType; + } + } + private PerformanceCounterCategoryType _counterSetType; + + + public string Description + { + get + { + return _description; + } + } + private string _description = ""; + + internal Dictionary CounterInstanceMapping + { + get + { + return _counterInstanceMapping; + } + } + private Dictionary _counterInstanceMapping; + + public StringCollection Paths + { + get + { + StringCollection retColl = new StringCollection(); + foreach (string counterName in this.CounterInstanceMapping.Keys) + { + string path; + if (CounterInstanceMapping[counterName].Length != 0) + { + path = (_machineName == ".") ? + ("\\" + _counterSetName + "(*)\\" + counterName) : + (_machineName + "\\" + _counterSetName + "(*)\\" + counterName); + } + else + { + path = (_machineName == ".") ? + ("\\" + _counterSetName + "\\" + counterName) : + (_machineName + "\\" + _counterSetName + "\\" + counterName); + } + retColl.Add(path); + } + + return retColl; + } + } + + public StringCollection PathsWithInstances + { + get + { + StringCollection retColl = new StringCollection(); + foreach (string counterName in CounterInstanceMapping.Keys) + { + foreach (string instanceName in CounterInstanceMapping[counterName]) + { + string path = (_machineName == ".") ? + ("\\" + _counterSetName + "(" + instanceName + ")\\" + counterName) : + (_machineName + "\\" + _counterSetName + "(" + instanceName + ")\\" + counterName); + retColl.Add(path); + } + } + return retColl; + } + } + + } + +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs new file mode 100644 index 000000000..9104fc838 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs @@ -0,0 +1,376 @@ +// +// Copyright (c) 2008 Microsoft Corporation. All rights reserved. +// + + +using System; +using System.Text; +using System.IO; +using System.Xml; +using System.Net; +using System.Management.Automation; +using System.ComponentModel; +using System.Reflection; +using System.Globalization; +using System.Management.Automation.Runspaces; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Security; +using System.Security.Principal; +using System.Resources; +using System.Threading; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Powershell.Commands.GetCounter.PdhNative; +using Microsoft.PowerShell.Commands.GetCounter; +using Microsoft.PowerShell.Commands.Diagnostics.Common; + + +namespace Microsoft.PowerShell.Commands +{ + + /// + /// Class that implements the Get-Counter cmdlet. + /// + [Cmdlet("Export", "Counter", DefaultParameterSetName = "ExportCounterSet", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=138337")] + public sealed class ExportCounterCommand : PSCmdlet + { + // + // Path parameter + // + [Parameter( + Mandatory = true, + Position = 0, + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources")] + [Alias("PSPath")] + + public string Path + { + get {return _path;} + set {_path = value;} + } + private string _path; + private string _resolvedPath; + + + // + // Format parameter. + // Valid strings are "blg", "csv", "tsv" (case-insensitive). + // + [Parameter( + Mandatory = false, + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [ValidateNotNull] + public string FileFormat + { + get {return _format;} + set {_format = value;} + } + private string _format = "BLG"; + + + + // + // MaxSize parameter + // Maximum output file size, in megabytes. + // + [Parameter( + HelpMessageBaseName = "GetEventResources")] + public UInt32 MaxSize + { + get {return _maxSize;} + set {_maxSize = value;} + } + private UInt32 _maxSize = 0; + + + // + // InputObject parameter + // + [Parameter( + Mandatory = true, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources")] + + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.ExportCounterCommand.InputObject", + Justification="A PerformanceCounterSampleSet[] is required here because Powershell supports arrays natively.")] + public PerformanceCounterSampleSet[] InputObject + { + get {return _counterSampleSets;} + set {_counterSampleSets = value;} + } + private PerformanceCounterSampleSet[] _counterSampleSets = new PerformanceCounterSampleSet[0]; + + + // + // Force switch + // + [Parameter( + HelpMessageBaseName = "GetEventResources")] + public SwitchParameter Force + { + get {return _force;} + set{_force = value;} + } + private SwitchParameter _force; + + // + // Circular switch + // + [Parameter( + HelpMessageBaseName = "GetEventResources")] + public SwitchParameter Circular + { + get {return _circular;} + set{_circular = value;} + } + private SwitchParameter _circular; + + + + private ResourceManager _resourceMgr = null; + + private PdhHelper _pdhHelper = null; + + private bool _stopping = false; + + private bool _queryInitialized = false; + + private PdhLogFileType _outputFormat = PdhLogFileType.PDH_LOG_TYPE_BINARY; + + // + // BeginProcessing() is invoked once per pipeline + // + protected override void BeginProcessing() + { + + _resourceMgr = new ResourceManager("GetEventResources", Assembly.GetExecutingAssembly()); + + // + // Determine the OS version: this cmdlet requires Windows 7 + // because it uses new Pdh functionality. + // + if (System.Environment.OSVersion.Version.Major < 6 || + (System.Environment.OSVersion.Version.Major == 6 && System.Environment.OSVersion.Version.Minor < 1)) + { + string msg = _resourceMgr.GetString("ExportCtrWin7Required"); + Exception exc = new Exception(msg); + ThrowTerminatingError(new ErrorRecord(exc, "ExportCtrWin7Required", ErrorCategory.NotImplemented, null)); + } + + _pdhHelper = new PdhHelper(System.Environment.OSVersion.Version.Major < 6); + + // + // Validate the Format and CounterSamples arguments + // + ValidateFormat(); + + if (Circular.IsPresent && _maxSize == 0) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterCircularNoMaxSize")); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "CounterCircularNoMaxSize", ErrorCategory.InvalidResult, null)); + } + + uint res = _pdhHelper.ConnectToDataSource(); + if (res != 0) + { + ReportPdhError(res, true); + } + + res = _pdhHelper.OpenQuery(); + if (res != 0) + { + ReportPdhError(res, true); + } + } + + // + // EndProcessing() is invoked once per pipeline + // + protected override void EndProcessing() + { + _pdhHelper.Dispose(); + } + + + /// + /// Handle Control-C + /// + protected override void StopProcessing() + { + _stopping = true; + _pdhHelper.Dispose(); + } + + // + // ProcessRecord() override. + // This is the main entry point for the cmdlet. + // When counter data comes from the pipeline, this gets invoked for each pipelined object. + // When it's passed in as an argument, ProcessRecord() is called once for the entire _counterSampleSets array. + // + protected override void ProcessRecord() + { + Debug.Assert(_counterSampleSets.Length != 0 && _counterSampleSets[0] != null); + + ResolvePath(); + + uint res = 0; + + if (!_queryInitialized) + { + if (_format.ToLower(CultureInfo.InvariantCulture).Equals("blg")) + { + res = _pdhHelper.AddRelogCounters(_counterSampleSets[0]); + } + else + { + res = _pdhHelper.AddRelogCountersPreservingPaths(_counterSampleSets[0]); + } + if (res != 0) + { + ReportPdhError(res, true); + } + + res = _pdhHelper.OpenLogForWriting(_resolvedPath, _outputFormat, Force.IsPresent, _maxSize * 1024 * 1024, Circular.IsPresent, null); + if (res == PdhResults.PDH_FILE_ALREADY_EXISTS) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterFileExists"), _resolvedPath); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "CounterFileExists", ErrorCategory.InvalidResult, null)); + } + else if (res == PdhResults.PDH_LOG_FILE_CREATE_ERROR) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("FileCreateFailed"), _resolvedPath); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "FileCreateFailed", ErrorCategory.InvalidResult, null)); + + } + else if (res == PdhResults.PDH_LOG_FILE_OPEN_ERROR) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("FileOpenFailed"), _resolvedPath); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "FileOpenFailed", ErrorCategory.InvalidResult, null)); + } + else if (res != 0) + { + ReportPdhError(res, true); + } + + _queryInitialized = true; + } + + + foreach (PerformanceCounterSampleSet set in _counterSampleSets) + { + _pdhHelper.ResetRelogValues(); + + foreach (PerformanceCounterSample sample in set.CounterSamples) + { + bool bUnknownKey = false; + res = _pdhHelper.SetCounterValue(sample, out bUnknownKey); + if (bUnknownKey) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterExportSampleNotInInitialSet"), sample.Path, _resolvedPath); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "CounterExportSampleNotInInitialSet", ErrorCategory.InvalidResult, null)); + } + else if (res != 0) + { + ReportPdhError(res, true); + } + + } + res = _pdhHelper.WriteRelogSample(set.Timestamp); + if (res != 0) + { + ReportPdhError(res, true); + } + + if (_stopping) + { + break; + } + } + } + + // ValidateFormat() helper. + // Validates Format argument: only "BLG", "TSV" and "CSV" are valid strings (case-insensitive) + // + private void ValidateFormat() + { + switch (_format.ToLower(CultureInfo.InvariantCulture)) + { + case "blg": + _outputFormat = PdhLogFileType.PDH_LOG_TYPE_BINARY; + break; + case "csv": + _outputFormat = PdhLogFileType.PDH_LOG_TYPE_CSV; + break; + case "tsv": + _outputFormat = PdhLogFileType.PDH_LOG_TYPE_TSV; + break; + default: + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterInvalidFormat"), _format); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "CounterInvalidFormat", ErrorCategory.InvalidArgument, null)); + break; + } + } + + private void ResolvePath() + { + try + { + Collection result = null; + result = SessionState.Path.GetResolvedPSPathFromPSPath(_path); + if (result.Count > 1) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ExportDestPathAmbiguous"), _path); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "ExportDestPathAmbiguous", ErrorCategory.InvalidArgument, null)); + } + + foreach (PathInfo currentPath in result) + { + _resolvedPath = currentPath.ProviderPath; + } + } + catch (ItemNotFoundException pathNotFound) + { + // + // This is an expected condition - we will be creating a new file + // + _resolvedPath = pathNotFound.ItemName; + } + } + + private void ReportPdhError(uint res, bool bTerminate) + { + string msg; + uint formatRes = CommonUtilities.FormatMessageFromModule(res, "pdh.dll", out msg); + if (formatRes != 0) + { + msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res); + } + Exception exc = new Exception (msg); + if (bTerminate) + { + ThrowTerminatingError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + else + { + WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + } + + } +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs new file mode 100644 index 000000000..b0eac6410 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs @@ -0,0 +1,645 @@ +// +// Copyright (c) 2008 Microsoft Corporation. All rights reserved. +// + + +using System; +using System.Text; +using System.IO; +using System.Xml; +using System.Net; +using System.Management.Automation; +using System.ComponentModel; +using System.Reflection; +using System.Globalization; +using System.Management.Automation.Runspaces; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Security; +using System.Security.Principal; +using System.Resources; +using System.Threading; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Powershell.Commands.GetCounter.PdhNative; +using Microsoft.PowerShell.Commands.GetCounter; +using Microsoft.PowerShell.Commands.Diagnostics.Common; + + +namespace Microsoft.PowerShell.Commands +{ + + /// + /// Class that implements the Get-Counter cmdlet. + /// + [Cmdlet(VerbsCommon.Get, "Counter", DefaultParameterSetName = "GetCounterSet", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=138335")] + public sealed class GetCounterCommand : PSCmdlet + { + // + // ListSet parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ParameterSetName="ListSetSet", + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [AllowEmptyCollection] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ListSet", + Justification="A string[] is required here because that is the type Powershell supports")] + + public string[] ListSet + { + get {return _listSet;} + set {_listSet = value;} + } + private string[] _listSet = {"*"}; + + + // + // Counter parameter + // + [Parameter( + Position = 0, + ParameterSetName="GetCounterSet", + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources")] + [AllowEmptyCollection] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ListSet", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] Counter + { + get {return _counter;} + set {_counter = value; + _defaultCounters = false;} + } + private string[] _counter = {@"\network interface(*)\bytes total/sec", + @"\processor(_total)\% processor time", + @"\memory\% committed bytes in use", + @"\memory\cache faults/sec", + @"\physicaldisk(_total)\% disk time", + @"\physicaldisk(_total)\current disk queue length"}; + private bool _defaultCounters = true; + + private List _accumulatedCounters = new List(); + + + // + // SampleInterval parameter. + // Defaults to 1 second. + // + [Parameter( + ParameterSetName="GetCounterSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [ValidateRange((int)1, int.MaxValue)] + public int SampleInterval + { + get {return _sampleInterval;} + set {_sampleInterval = value;} + } + private int _sampleInterval = 1; + + + // + // MaxSamples parameter + // + private const Int64 KEEP_ON_SAMPLING = -1; + [Parameter( + ParameterSetName="GetCounterSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [ValidateRange((Int64)1, Int64.MaxValue)] + public Int64 MaxSamples + { + get {return _maxSamples;} + set + { + _maxSamples = value; + _maxSamplesSpecified = true; + } + } + private Int64 _maxSamples = 1; + private bool _maxSamplesSpecified = false; + + + // + // Continuous switch + // + [Parameter(ParameterSetName="GetCounterSet")] + public SwitchParameter Continuous + { + get {return _continuous;} + set{_continuous = value;} + } + private bool _continuous = false; + + + // + // ComputerName parameter + // + [Parameter( + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + [ValidateNotNull] + [AllowEmptyCollection] + [Alias("Cn")] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ComputerName", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] ComputerName + { + get {return _computerName;} + set {_computerName = value;} + } + private string[] _computerName = new string[0]; + + private ResourceManager _resourceMgr = null; + + private PdhHelper _pdhHelper = null; + + private EventWaitHandle _cancelEventArrived = new EventWaitHandle(false, EventResetMode.ManualReset); + + // Culture identifier(s) + private const int FrenchCultureId = 0x040C; + // The localized Pdh resource strings might use Unicode characters that are different from + // what the user can type with the keyboard to represent a special character. + // + // e.g. the apostrophe in Frence UI culture: it's [char]39 from keyboard, but it's [char]8217 + // in the resource strings. + // + // With this dictionary, we can add special mapping if we find other special cases in the future. + private readonly Dictionary>> _cultureAndSpecialCharacterMap = + new Dictionary>>() + { + { + FrenchCultureId, new List>() + { + // 'APOSTROPHE' to 'RIGHT SINGLE QUOTATION MARK' + new Tuple((char) 0x0027, (char) 0x2019), + // 'MODIFIER LETTER APOSTROPHE' to 'RIGHT SINGLE QUOTATION MARK' + new Tuple((char) 0x02BC, (char) 0x2019), + // 'HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT' to 'RIGHT SINGLE QUOTATION MARK' + new Tuple((char) 0x275C, (char) 0x2019), + } + } + }; + + // + // BeginProcessing() is invoked once per pipeline + // + protected override void BeginProcessing() + { + _resourceMgr = new ResourceManager("GetEventResources", Assembly.GetExecutingAssembly()); + + _pdhHelper = new PdhHelper(System.Environment.OSVersion.Version.Major < 6); + uint res = _pdhHelper.ConnectToDataSource(); + if (res != 0) + { + ReportPdhError(res, true); + return; + } + + + if (Continuous.IsPresent && _maxSamplesSpecified) + { + Exception exc = new Exception(string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterContinuousOrMaxSamples"))); + ThrowTerminatingError (new ErrorRecord(exc, "CounterContinuousOrMaxSamples", ErrorCategory.InvalidArgument, null)); + } + } + + // + // EndProcessing() is invoked once per pipeline + // + protected override void EndProcessing() + { + if (ParameterSetName == "GetCounterSet") + { + ProcessGetCounter(); + } + + _pdhHelper.Dispose(); + } + + + // + // Handle Control-C + // + protected override void StopProcessing() + { + _cancelEventArrived.Set(); + _pdhHelper.Dispose(); + } + + // + // ProcessRecord() override. + // This is the main entry point for the cmdlet. + // + protected override void ProcessRecord() + { + try + { + switch (ParameterSetName) + { + case "ListSetSet": + ProcessListSet(); + break; + + case "GetCounterSet": + AccumulatePipelineCounters(); + break; + + default: + Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + break; + } + } + catch (Exception exc) + { + ThrowTerminatingError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + } + + // + // AccumulatePipelineCounters() accumulates counter paths in the pipeline scenario: + // we do not want to start sampling until all the counters are supplied. + // + private void AccumulatePipelineCounters() + { + _accumulatedCounters.AddRange(_counter); + } + + // + // ProcessListSet() does the work to process ListSet parameter set. + // + private void ProcessListSet() + { + if (_computerName.Length == 0) + { + ProcessListSetPerMachine(null); + } + else foreach (string machine in _computerName) + { + ProcessListSetPerMachine(machine); + } + } + + // + // ProcessListSetPerMachine() helper lists counter sets on a machine. + // NOTE: machine argument should be NULL for the local machine + // + private void ProcessListSetPerMachine(string machine) + { + StringCollection counterSets = new StringCollection(); + uint res = _pdhHelper.EnumObjects(machine, ref counterSets); + if (res != 0) + { + //add an error message + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoCounterSetsOnComputer"), machine, res); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "NoCounterSetsOnComputer", ErrorCategory.InvalidResult, machine)); + return; + } + + CultureInfo culture = Thread.CurrentThread.CurrentUICulture; + List> characterReplacementList = null; + StringCollection validPaths = new StringCollection(); + + _cultureAndSpecialCharacterMap.TryGetValue(culture.LCID, out characterReplacementList); + + foreach (string pattern in _listSet) + { + bool bMatched = false; + string normalizedPattern = pattern; + + if (characterReplacementList != null) + { + foreach (Tuple pair in characterReplacementList) + { + normalizedPattern = normalizedPattern.Replace(pair.Item1, pair.Item2); + } + } + + WildcardPattern wildLogPattern = new WildcardPattern(normalizedPattern, WildcardOptions.IgnoreCase); + + foreach (string counterSet in counterSets) + { + if (!wildLogPattern.IsMatch(counterSet)) + { + continue; + } + + StringCollection counterSetCounters = new StringCollection(); + StringCollection counterSetInstances = new StringCollection(); + + res = _pdhHelper.EnumObjectItems(machine, counterSet, ref counterSetCounters, ref counterSetInstances); + if (res == PdhResults.PDH_ACCESS_DENIED) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterSetEnumAccessDenied"), counterSet); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "CounterSetEnumAccessDenied", ErrorCategory.InvalidResult, null)); + continue; + } + else if (res != 0) + { + ReportPdhError(res, false); + continue; + } + + string[] instanceArray = new string[counterSetInstances.Count]; + int i = 0; + foreach (string instance in counterSetInstances) + { + instanceArray[i++] = instance; + } + + // + // Special case: no instances present: change to * to create a valid paths + // + if (instanceArray.Length == 1 && + instanceArray[0].Length == 0) + { + instanceArray[0] = "*"; + } + + Dictionary counterInstanceMapping = new Dictionary(); + foreach (string counter in counterSetCounters) + { + if (!counterInstanceMapping.ContainsKey(counter)) + { + counterInstanceMapping.Add(counter, instanceArray); + } + } + + PerformanceCounterCategoryType categoryType = PerformanceCounterCategoryType.Unknown; + if (counterSetInstances.Count > 1) + { + categoryType = PerformanceCounterCategoryType.MultiInstance; + } + else //if (counterSetInstances.Count == 1) //??? + { + categoryType = PerformanceCounterCategoryType.SingleInstance; + } + + string setHelp = _pdhHelper.GetCounterSetHelp(machine, counterSet); + + CounterSet setObj = new CounterSet(counterSet, machine, categoryType, setHelp, ref counterInstanceMapping); + WriteObject (setObj); + bMatched = true; + } + + if (!bMatched) + { + string msg = _resourceMgr.GetString("NoMatchingCounterSetsFound"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, + machine ?? "localhost", normalizedPattern)); + WriteError (new ErrorRecord(exc, "NoMatchingCounterSetsFound", ErrorCategory.ObjectNotFound, null)); + } + } + + } + + // + // ProcessGetCounter() + // Does the work to process GetCounterSet parameter set. + // + private void ProcessGetCounter() + { + // 1. Combine machine names with paths, if needed, to construct full paths + // 2. Translate default paths into current locale + // 3. Expand wildcards and validate the paths and write errors for any invalid paths + // 4. OpenQuery/ AddCounters + // 5. Skip the first reading + + CultureInfo culture = Thread.CurrentThread.CurrentUICulture; + List> characterReplacementList = null; + List paths = CombineMachinesAndCounterPaths(); + uint res = 0; + + if (!_defaultCounters) + { + _cultureAndSpecialCharacterMap.TryGetValue(culture.LCID, out characterReplacementList); + } + + + StringCollection allExpandedPaths = new StringCollection(); + foreach (string path in paths) + { + string localizedPath = path; + if (_defaultCounters) + { + res = _pdhHelper.TranslateLocalCounterPath(path, out localizedPath); + if (res != 0) + { + string msg = string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterPathTranslationFailed"), res); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "CounterPathTranslationFailed", ErrorCategory.InvalidResult, null)); + + localizedPath = path; + } + } + else if (characterReplacementList != null) + { + foreach (Tuple pair in characterReplacementList) + { + localizedPath = localizedPath.Replace(pair.Item1, pair.Item2); + } + } + + StringCollection expandedPaths; + res = _pdhHelper.ExpandWildCardPath(localizedPath, out expandedPaths); + if (res != 0) + { + WriteDebug("Could not expand path " + localizedPath); + ReportPdhError(res, false); + continue; + } + + foreach (string expandedPath in expandedPaths) + { + if (!_pdhHelper.IsPathValid(expandedPath)) + { + string msg = string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterPathIsInvalid"), localizedPath); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "CounterPathIsInvalid", ErrorCategory.InvalidResult, null)); + + continue; + } + allExpandedPaths.Add(expandedPath); + } + } + if (allExpandedPaths.Count == 0) + { + return; + } + + res = _pdhHelper.OpenQuery(); + if (res != 0) + { + ReportPdhError(res, false); + } + res = _pdhHelper.AddCounters(ref allExpandedPaths, true); + if (res != 0) + { + ReportPdhError(res, true); + + return; + } + + PerformanceCounterSampleSet nextSet; + + bool bSkip = true; + uint sampleReads = 0; + + if (Continuous.IsPresent) + { + _maxSamples = KEEP_ON_SAMPLING; + } + + while (true) + { + // read the first set just to get the initial values + res = _pdhHelper.ReadNextSet (out nextSet, bSkip); + + if (res == 0) + { + //Display valid data + if (!bSkip) + { + WriteSampleSetObject (nextSet); + sampleReads++; + } + + //Don't need to skip anymore + bSkip = false; + + } + else if (res == PdhResults.PDH_NO_DATA || res == PdhResults.PDH_INVALID_DATA) + { + // The provider may not be running. + // We should keep on trying - but skip the next valid reading. + + ReportPdhError(res, false); + + bSkip = true; + + // Count this failed attempt as a sample: + sampleReads++; + } + else + { + // Unexpected error, return + ReportPdhError(res, true); + return; + } + + if (_maxSamples != KEEP_ON_SAMPLING && sampleReads >= _maxSamples) + { + break; + } + + bool cancelled = _cancelEventArrived.WaitOne((int)_sampleInterval * 1000, true); + if (cancelled) + { + break; + } + + } + } + + private void ReportPdhError(uint res, bool bTerminate) + { + string msg; + uint formatRes = CommonUtilities.FormatMessageFromModule(res, "pdh.dll", out msg); + if (formatRes != 0) + { + msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res); + } + Exception exc = new Exception (msg); + if (bTerminate) + { + ThrowTerminatingError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + else + { + WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + } + + + // + // CombineMachinesAndCounterPaths() helper. + // For paths that do not contain machine names, creates a path for each machine in machineNames. + // Paths already containing a machine name will be preserved. + // + private List CombineMachinesAndCounterPaths () + { + List retColl = new List(); + + if (_computerName.Length == 0) + { + retColl.AddRange(_accumulatedCounters); + return retColl; + } + + foreach (string path in _accumulatedCounters) + { + if (path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) //NOTE: can we do anything smarter here? + { + retColl.Add (path); + } + else + { + foreach (string machine in _computerName) + { + if (machine.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) + { + retColl.Add (machine + "\\" + path); + } + else + { + retColl.Add ("\\\\" + machine + "\\" + path); + } + } + } + } + + return retColl; + } + + // + // WriteSampleSetObject() helper. + // In addition to writing the PerformanceCounterSampleSet object, + // it writes a single error if one of the samples has an invalid (non-zero) status. + // + private void WriteSampleSetObject(PerformanceCounterSampleSet set) + { + foreach (PerformanceCounterSample sample in set.CounterSamples) + { + if (sample.Status != 0) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterSampleDataInvalid")); + Exception exc = new Exception(msg); + WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + break; + } + } + WriteObject (set); + } + + } +} + diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs new file mode 100644 index 000000000..019c0b014 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs @@ -0,0 +1,2101 @@ +// +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. +// + +using System; +using System.Xml; +using System.Net; +using System.Management.Automation; +using System.Reflection; +using System.Globalization; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Eventing.Reader; +using System.Security.Principal; +using System.Resources; +using System.Diagnostics.CodeAnalysis; + +[assembly: CLSCompliant(false)] + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Class that implements the Get-WinEvent cmdlet. + /// + [Cmdlet(VerbsCommon.Get, "WinEvent" , DefaultParameterSetName = "GetLogSet", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=138336")] + public sealed class GetWinEventCommand : PSCmdlet + { + // + // ListLog parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ParameterSetName="ListLogSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ListLogParamHelp")] + [AllowEmptyCollection] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.ListLog", + Justification="A string[] is required here because that is the type Powershell supports")] + + public string[] ListLog + { + get {return _listLog;} + set {_listLog = value;} + } + private string[] _listLog = {"*"}; + + // + // GetLog parameter + // + [Parameter( + Position = 0, + ParameterSetName="GetLogSet", + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "GetLogParamHelp")] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.LogName", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] LogName + { + get {return _logName;} + set {_logName = value;} + } + private string[] _logName = {"*"}; + + + // + // ListProvider parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ParameterSetName="ListProviderSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ListProviderParamHelp")] + [AllowEmptyCollection] + + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.ListProvider", + Justification="A string[] is required here because that is the type Powershell supports")] + + public string[] ListProvider + { + get {return _listProvider;} + set {_listProvider = value;} + } + private string[] _listProvider = {"*"}; + + + // + // ProviderName parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ParameterSetName="GetProviderSet", + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "GetProviderParamHelp")] + + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.ProviderName", + Justification="A string[] is required here because that is the type Powershell supports")] + + public string[] ProviderName + { + get {return _providerName;} + set {_providerName = value;} + } + private string[] _providerName; + + + // + // Path parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ParameterSetName="FileSet", + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "PathParamHelp")] + + [Alias("PSPath")] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.Path", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] Path + { + get {return _path;} + set {_path = value;} + } + private string[] _path; + + + // + // MaxEvents parameter + // + [Parameter( + ParameterSetName="FileSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "MaxEventsParamHelp")] + [Parameter( + ParameterSetName="GetProviderSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "MaxEventsParamHelp")] + [Parameter( + ParameterSetName="GetLogSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "MaxEventsParamHelp")] + [Parameter( + ParameterSetName="HashQuerySet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "MaxEventsParamHelp")] + [Parameter( + ParameterSetName="XmlQuerySet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "MaxEventsParamHelp")] + [ValidateRange((Int64)1, Int64.MaxValue)] + public Int64 MaxEvents + { + get {return _maxEvents;} + set {_maxEvents = value;} + } + private Int64 _maxEvents = -1; + + // + // ComputerName parameter + // + [Parameter( + ParameterSetName="ListProviderSet", + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + [Parameter( + ParameterSetName="GetProviderSet", + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + [Parameter( + ParameterSetName="ListLogSet", + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + [Parameter( + ParameterSetName="GetLogSet", + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + [Parameter( + ParameterSetName="HashQuerySet", + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + [Parameter( + ParameterSetName="XmlQuerySet", + HelpMessageBaseName = "GetEventResources", + HelpMessageResourceId = "ComputerNameParamHelp")] + + [ValidateNotNull] + [Alias("Cn")] + public string ComputerName + { + get {return _computerName;} + set {_computerName = value;} + } + private string _computerName = string.Empty; + + // + // Credential parameter + // + [Parameter(ParameterSetName="ListProviderSet")] + [Parameter(ParameterSetName="GetProviderSet")] + [Parameter(ParameterSetName="ListLogSet")] + [Parameter(ParameterSetName="GetLogSet")] + [Parameter(ParameterSetName="HashQuerySet")] + [Parameter(ParameterSetName="XmlQuerySet")] + [Parameter(ParameterSetName="FileSet")] + [Credential] + public PSCredential Credential + { + get {return _credential;} + set {_credential = value;} + } + private PSCredential _credential = PSCredential.Empty; + + + // + // FilterXPath parameter + // + [Parameter( + ParameterSetName="FileSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [Parameter( + ParameterSetName="GetProviderSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [Parameter( + ParameterSetName="GetLogSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [ValidateNotNull] + public string FilterXPath + { + get {return _filter;} + set {_filter = value;} + } + private string _filter = "*"; + + // + // FilterXml parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + ParameterSetName = "XmlQuerySet", + HelpMessageBaseName = "GetEventResources")] + + [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.FilterXml", + Justification="An XmlDocument is required here because that is the type Powershell supports")] + + public XmlDocument FilterXml + { + get {return _xmlQuery;} + set {_xmlQuery = value;} + } + private XmlDocument _xmlQuery = null; + + + // + // FilterHashtable parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + ParameterSetName = "HashQuerySet", + HelpMessageBaseName = "GetEventResources")] + + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetEvent.FilterHashtable", + Justification="A string[] is required here because that is the type Powershell supports")] + + public Hashtable[] FilterHashtable + { + get {return _selector;} + set {_selector = value;} + } + private Hashtable[] _selector; + + // + // Force switch + // + [Parameter(ParameterSetName="ListLogSet")] + [Parameter(ParameterSetName="GetProviderSet")] + [Parameter(ParameterSetName="GetLogSet")] + + [Parameter(ParameterSetName="HashQuerySet")] + public SwitchParameter Force + { + get {return _force;} + set{_force = value;} + } + private SwitchParameter _force; + + // + // Oldest switch + // + [Parameter(ParameterSetName="FileSet")] + [Parameter(ParameterSetName="GetProviderSet")] + [Parameter(ParameterSetName="GetLogSet")] + + [Parameter(ParameterSetName="HashQuerySet")] + [Parameter(ParameterSetName="XmlQuerySet")] + public SwitchParameter Oldest + { + get {return _oldest;} + set{_oldest = value;} + } + private bool _oldest = false; + + + // + // Query builder constant strings + // + private const string queryListOpen= ""; + private const string queryListClose= ""; + private const string queryTemplate = ""; + private const string queryOpenerTemplate = ""; + private const string propOpen = "["; + private const string propClose = "]"; + private const string filePrefix = "file://"; + + // + // Other private members and constants + // + private ResourceManager _resourceMgr = null; + private Dictionary _providersByLogMap = new Dictionary(); + + private StringCollection _logNamesMatchingWildcard = null; + private StringCollection _resolvedPaths = new StringCollection(); + + private List _accumulatedLogNames = new List(); + private List _accumulatedProviderNames = new List(); + private List _accumulatedFileNames = new List(); + + private const uint MAX_EVENT_BATCH=100; + + // + // Hashtable query key names + // + private const string hashkey_logname_lc="logname"; + private const string hashkey_providername_lc="providername"; + private const string hashkey_path_lc="path"; + private const string hashkey_keywords_lc="keywords"; + private const string hashkey_id_lc="id"; + private const string hashkey_level_lc="level"; + private const string hashkey_starttime_lc="starttime"; + private const string hashkey_endtime_lc="endtime"; + private const string hashkey_userid_lc="userid"; + private const string hashkey_data_lc="data"; + + + // + // BeginProcessing() is invoked once per pipeline: we will load System.Core.dll here + // + protected override void BeginProcessing() + { + _resourceMgr = new ResourceManager("GetEventResources", typeof(GetWinEventCommand).GetTypeInfo().Assembly); + } + + + // + // EndProcessing() is invoked once per pipeline + // + protected override void EndProcessing() + { + + switch (ParameterSetName) + { + case "GetLogSet": + ProcessGetLog(); + break; + + case "FileSet": + ProcessFile(); + break; + + case "GetProviderSet": + ProcessGetProvider(); + break; + + default: + break; + } + } + + + // + // ProcessRecord() override. + // This is the main entry point for the cmdlet. + // + protected override void ProcessRecord() + { + switch (ParameterSetName) + { + case "ListLogSet": + ProcessListLog(); + break; + + case "ListProviderSet": + ProcessListProvider(); + break; + + case "GetLogSet": + AccumulatePipelineLogNames(); + break; + + case "FileSet": + AccumulatePipelineFileNames(); + break; + + case "HashQuerySet": + ProcessHashQuery(); + break; + + case "GetProviderSet": + AccumulatePipelineProviderNames(); + break; + + case "XmlQuerySet": + ProcessFilterXml(); + break; + + default: + WriteDebug(string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + break; + } + + } + + + // + // AccumulatePipelineCounters() accumulates log names in the pipeline scenario: + // we do not want to construct a query until all the log names are supplied. + // + private void AccumulatePipelineLogNames() + { + _accumulatedLogNames.AddRange(_logName); + } + + // + // AccumulatePipelineProviderNames() accumulates provider names in the pipeline scenario: + // we do not want to construct a query until all the provider names are supplied. + // + private void AccumulatePipelineProviderNames() + { + _accumulatedProviderNames.AddRange(_logName); + } + + // + // AccumulatePipelineFileNames() accumulates log file paths in the pipeline scenario: + // we do not want to construct a query until all the file names are supplied. + // + private void AccumulatePipelineFileNames() + { + _accumulatedFileNames.AddRange(_logName); + } + + // + // Process GetLog parameter set + // + private void ProcessGetLog() + { + EventLogSession eventLogSession = CreateSession(); + + FindLogNamesMatchingWildcards (eventLogSession, _accumulatedLogNames); + if (_logNamesMatchingWildcard.Count == 0) + { + return; + } + + EventLogQuery logQuery; + if (_logNamesMatchingWildcard.Count > 1) + { + string query = BuildStructuredQuery(eventLogSession); + logQuery = new EventLogQuery(null, PathType.LogName, query); + logQuery.TolerateQueryErrors = true; + } + else + { + logQuery = new EventLogQuery(_logNamesMatchingWildcard[0], PathType.LogName, _filter); + } + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; + + EventLogReader readerObj = new EventLogReader(logQuery); + + if (readerObj != null) + { + ReadEvents (readerObj); + } + } + + + // + // Process GetProviderSet parameter set + // + private void ProcessGetProvider() + { + EventLogSession eventLogSession = CreateSession(); + + FindProvidersByLogForWildcardPatterns (eventLogSession, _providerName); + + if (_providersByLogMap.Count == 0) + { + // + // Just return: errors already written above for each unmatched provider name pattern. + // + return; + } + + + EventLogQuery logQuery = null; + if (_providersByLogMap.Count > 1) + { + string query = BuildStructuredQuery(eventLogSession); + logQuery = new EventLogQuery(null, PathType.LogName, query); + logQuery.TolerateQueryErrors = true; + } + else + { + // + // There's only one key at this point, but we need an enumerator to get to it. + // + foreach (string log in _providersByLogMap.Keys) + { + logQuery = new EventLogQuery(log, PathType.LogName, AddProviderPredicatesToFilter(_providersByLogMap[log])); + WriteVerbose (string.Format(CultureInfo.InvariantCulture, "Log {0} will be queried", log)); + } + } + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest;; + + EventLogReader readerObj = new EventLogReader(logQuery); + if (readerObj != null) + { + ReadEvents (readerObj); + } + } + + + // + // Process ListLog parameter set + // + private void ProcessListLog() + { + + EventLogSession eventLogSession = CreateSession(); + + foreach (string logPattern in _listLog) + { + bool bMatchFound = false; + + foreach (string logName in eventLogSession.GetLogNames()) + { + WildcardPattern wildLogPattern = new WildcardPattern(logPattern, WildcardOptions.IgnoreCase); + + if (((!WildcardPattern.ContainsWildcardCharacters(logPattern)) + && string.Equals(logPattern, logName, StringComparison.CurrentCultureIgnoreCase)) + || + (wildLogPattern.IsMatch(logName))) + { + try + { + EventLogConfiguration logObj = new EventLogConfiguration(logName, eventLogSession); + + // + // Skip direct channels matching the wildcard unless -Force is present. + // + if (!Force.IsPresent && + WildcardPattern.ContainsWildcardCharacters(logPattern) && + (logObj.LogType == EventLogType.Debug || + logObj.LogType == EventLogType.Analytical)) + { + continue; + } + + EventLogInformation logInfoObj = eventLogSession.GetLogInformation(logName, PathType.LogName); + + PSObject outputObj = new PSObject(logObj ); + + outputObj.Properties.Add(new PSNoteProperty( "FileSize", logInfoObj.FileSize)); + outputObj.Properties.Add(new PSNoteProperty( "IsLogFull", logInfoObj.IsLogFull)); + outputObj.Properties.Add(new PSNoteProperty( "LastAccessTime", logInfoObj.LastAccessTime)); + outputObj.Properties.Add(new PSNoteProperty( "LastWriteTime", logInfoObj.LastWriteTime)); + outputObj.Properties.Add(new PSNoteProperty( "OldestRecordNumber", logInfoObj.OldestRecordNumber)); + outputObj.Properties.Add(new PSNoteProperty( "RecordCount", logInfoObj.RecordCount)); + + WriteObject (outputObj); + bMatchFound = true; + } + catch (Exception exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("LogInfoUnavailable"), + logName, exc.Message); + Exception outerExc = new Exception (msg, exc); + WriteError(new ErrorRecord( outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); + continue; + } + } + } + if (!bMatchFound) + { + string msg = _resourceMgr.GetString("NoMatchingLogsFound"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, this._computerName, logPattern)); + WriteError(new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, null)); + } + } + + } + + // + // Process ListProvider parameter set + // + private void ProcessListProvider() + { + EventLogSession eventLogSession = CreateSession(); + + foreach (string provPattern in _listProvider) + { + bool bMatchFound = false; + + foreach (string provName in eventLogSession.GetProviderNames()) + { + + WildcardPattern wildProvPattern = new WildcardPattern(provPattern, WildcardOptions.IgnoreCase); + + if (((!WildcardPattern.ContainsWildcardCharacters(provPattern)) + && string.Equals(provPattern, provName, StringComparison.CurrentCultureIgnoreCase)) + || + (wildProvPattern.IsMatch(provName))) + { + try + { + ProviderMetadata provObj = new ProviderMetadata(provName, eventLogSession, CultureInfo.CurrentCulture); + WriteObject (provObj); + bMatchFound = true; + } + catch (System.Diagnostics.Eventing.Reader.EventLogException exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("ProviderMetadataUnavailable"), + provName, exc.Message); + Exception outerExc = new Exception (msg, exc); + WriteError(new ErrorRecord( outerExc, "ProviderMetadataUnavailable", ErrorCategory.NotSpecified, null)); + continue; + } + } + } + + if (!bMatchFound) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoMatchingProvidersFound"), + this._computerName, provPattern); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord( exc, "NoMatchingProvidersFound", ErrorCategory.ObjectNotFound, null)); + } + } + + } + + // + // Process FilterXml parameter set + // + private void ProcessFilterXml() + { + EventLogSession eventLogSession = CreateSession(); + + if (!Oldest.IsPresent) + { + // + // Do minimal parsing of xmlQuery to determine if any direct channels or ETL files are in it. + // + XmlElement root = _xmlQuery.DocumentElement; + XmlNodeList queryNodes = root.SelectNodes("//Query//Select"); + foreach (XmlNode queryNode in queryNodes) + { + XmlAttributeCollection attribs = queryNode.Attributes; + foreach (XmlAttribute attrib in attribs) + { + if (attrib.Name.Equals("Path", StringComparison.OrdinalIgnoreCase)) + { + string logName = attrib.Value; + + if (logName.StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase)) + { + TerminateForNonEvtxFileWithoutOldest(logName); + } + + ValidateLogName(logName, eventLogSession); + } + } + } + } + + EventLogQuery logQuery = new EventLogQuery(null, PathType.LogName, _xmlQuery.InnerXml); + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; + + EventLogReader readerObj = new EventLogReader(logQuery); + if (readerObj != null) + { + ReadEvents (readerObj); + } + } + + + // + // Process FileSet parameter set + // + private void ProcessFile() + { + + EventLogSession eventLogSession = CreateSession(); + + // + // At this point, _path array contains paths that might have wildcards, + // environment variables or PS drives. Let's resolve those. + // + for (int i = 0; i < _path.Length; i++) + { + StringCollection resolvedPaths = ValidateAndResolveFilePath (_path[i]); + foreach (string resolvedPath in resolvedPaths) + { + _resolvedPaths.Add (resolvedPath); + WriteVerbose (string.Format(CultureInfo.InvariantCulture, "Found file {0}", resolvedPath)); + } + } + + EventLogQuery logQuery = null; + if (_resolvedPaths.Count == 0) + { + return; + } + else if (_resolvedPaths.Count > 1) + { + string query = BuildStructuredQuery(eventLogSession); + logQuery = new EventLogQuery(null, PathType.FilePath, query); + logQuery.TolerateQueryErrors = true; + } + else + { + logQuery = new EventLogQuery(_resolvedPaths[0], PathType.FilePath, _filter); + } + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; + + EventLogReader readerObj = new EventLogReader(logQuery); + if (readerObj != null) + { + ReadEvents (readerObj); + } + } + + // + // Process HashQuerySet parameter set + // + private void ProcessHashQuery() + { + CheckHashTablesForNullValues(); + + EventLogSession eventLogSession = CreateSession(); + + string query = BuildStructuredQuery(eventLogSession); + if (query.Length == 0) + { + return; + } + + EventLogQuery logQuery = new EventLogQuery(null, PathType.FilePath, query); + logQuery.Session = eventLogSession; + logQuery.TolerateQueryErrors = true; + logQuery.ReverseDirection = !_oldest; + + EventLogReader readerObj = new EventLogReader(logQuery); + if (readerObj != null) + { + ReadEvents (readerObj); + } + } + + // + // CreateSession creates an EventLogSession connected to a target machine or localhost. + // If _credential argment is PSCredential.Empty, the session will be created for the current context. + // + private EventLogSession CreateSession () + { + + EventLogSession eventLogSession = null; + + if (_computerName == string.Empty) + { + // Set _computerName to "localhost" for future error messages, + // but do not use it for the connection to avoid RPC overhead. + _computerName = "localhost"; + + if (_credential == PSCredential.Empty) + { + return new EventLogSession(); + } + } + else if (_credential == PSCredential.Empty) + { + return new EventLogSession(_computerName); + } + + // If we are here, either both computer name and credential were passed initially, + // or credential only - we will use it with "localhost" + + NetworkCredential netCred = (NetworkCredential) _credential; + eventLogSession = new EventLogSession(_computerName, + netCred.Domain, + netCred.UserName, + _credential.Password, + SessionAuthentication.Default + ); + // + // Force the destruction of cached password + // + netCred.Password = ""; + + return eventLogSession; + } + + + // + // ReadEvents helper. + // + private void ReadEvents(EventLogReader readerObj) + { + Int64 numEvents = 0; + EventRecord evtObj = null; + + while (true) + { + try + { + evtObj = readerObj.ReadEvent(); + } + catch (Exception exc) + { + WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); + continue; + } + if (evtObj == null) + { + break; + } + if (_maxEvents != -1 && numEvents >= _maxEvents) + { + break; + } + + PSObject outputObj = new PSObject(evtObj); + + string evtMessage = _resourceMgr.GetString("NoEventMessage"); + try + { + evtMessage = evtObj.FormatDescription(); + } + catch (Exception exc) + { + WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); + } + outputObj.Properties.Add(new PSNoteProperty("Message", evtMessage)); + + + // + // Enumerate the object one level to get to event payload + // + WriteObject (outputObj, true); + numEvents++; + } + + if (numEvents == 0) + { + string msg = _resourceMgr.GetString("NoMatchingEventsFound"); + Exception exc = new Exception(msg); + WriteError(new ErrorRecord(exc, "NoMatchingEventsFound", ErrorCategory.ObjectNotFound, null)); + } + } + + + + // + // BuildStructuredQuery() builds a structured query from cmdlet arguments. + // + private string BuildStructuredQuery(EventLogSession eventLogSession) + { + string result = ""; + + switch (ParameterSetName) + { + case "ListLogSet": + break; + + case "ListProviderSet": + break; + + case "GetProviderSet": + { + result = queryListOpen; + uint queryId = 0; + + foreach (string log in _providersByLogMap.Keys) + { + string providerFilter = AddProviderPredicatesToFilter(_providersByLogMap[log]); + string addedQuery; + addedQuery = string.Format(CultureInfo.InvariantCulture, queryTemplate, new object[]{queryId++, log, providerFilter}); + result += addedQuery; + } + result += queryListClose; + } + break; + + case "GetLogSet": + { + result = queryListOpen; + uint queryId = 0; + foreach (string log in _logNamesMatchingWildcard) + { + string addedQuery; + addedQuery = string.Format(CultureInfo.InvariantCulture, queryTemplate, new object[]{queryId++, log, _filter}); + result += addedQuery; + } + result += queryListClose; + } + break; + + case "FileSet": + { + result = queryListOpen; + uint queryId = 0; + foreach (string filePath in _resolvedPaths) + { + string properFilePath = filePrefix + filePath; + string addedQuery; + addedQuery = string.Format(CultureInfo.InvariantCulture, queryTemplate, new object[]{queryId++, properFilePath, _filter}); + result += addedQuery; + } + result += queryListClose; + } + break; + + case "HashQuerySet": + result = BuildStructuredQueryFromHashTable(eventLogSession); + break; + + default: + WriteDebug (string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + break; + } + + WriteVerbose (string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("QueryTrace"), result)); + + return result; + } + + // + // BuildStructuredQueryFromHashTable() helper. + // Builds a structured query from the hashtable (Selector) argument. + // + private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession) + { + string result = ""; + + result = queryListOpen; + + uint queryId = 0; + + foreach (Hashtable hash in _selector) + { + string xpathString = ""; + + CheckHashTableForQueryPathPresence(hash); + + // + // Local queriedLogsQueryMap will hold names of logs or files to be queried + // mapped to the actual query strings being built up. + // + Dictionary queriedLogsQueryMap = new Dictionary(); + + // + // Process log, _path, or provider parameters first + // to create initial partially-filled query templates. + // Error out for direct channels unless -oldest is present. + // + // Order is important! Process "providername" key after "logname" and "file". + // + if (hash.ContainsKey(hashkey_logname_lc)) + { + List logPatterns = new List(); + if (hash[hashkey_logname_lc] is Array) + { + foreach (Object elt in (Array)hash[hashkey_logname_lc]) + { + logPatterns.Add(elt.ToString()); + } + } + else + { + logPatterns.Add(hash[hashkey_logname_lc].ToString()); + } + + FindLogNamesMatchingWildcards(eventLogSession, logPatterns); + + foreach (string logName in _logNamesMatchingWildcard) + { + queriedLogsQueryMap.Add(logName.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, logName)); + } + } + if (hash.ContainsKey(hashkey_path_lc)) + { + if (hash[hashkey_path_lc] is Array) + { + foreach (Object elt in (Array)hash[hashkey_path_lc]) + { + StringCollection resolvedPaths = ValidateAndResolveFilePath (elt.ToString()); + foreach (string resolvedPath in resolvedPaths) + { + queriedLogsQueryMap.Add(filePrefix + resolvedPath.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, filePrefix + resolvedPath)); + } + } + } + else + { + StringCollection resolvedPaths = ValidateAndResolveFilePath (hash[hashkey_path_lc].ToString()); + foreach (string resolvedPath in resolvedPaths) + { + queriedLogsQueryMap.Add(filePrefix + resolvedPath.ToLowerInvariant(), + string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, filePrefix + resolvedPath)); + } + } + } + if (hash.ContainsKey(hashkey_providername_lc)) + { + + List provPatterns = new List(); + if (hash[hashkey_providername_lc] is Array) + { + foreach (Object elt in (Array)hash[hashkey_providername_lc]) + { + provPatterns.Add(elt.ToString()); + } + } + else + { + provPatterns.Add(hash[hashkey_providername_lc].ToString()); + } + + FindProvidersByLogForWildcardPatterns(eventLogSession, provPatterns); + + // + // If "providername" key is used alone, we will construct a query across all of the providers' logs. + // Otherwise, we will use the provider names to add predicates to "logname" and "path" queries. + // + if (!hash.ContainsKey(hashkey_path_lc) && !hash.ContainsKey(hashkey_logname_lc)) + { + foreach (string keyLogName in _providersByLogMap.Keys) + { + string providersPredicate = BuildProvidersPredicate(_providersByLogMap[keyLogName]); + string query = string.Format(CultureInfo.InvariantCulture, queryOpenerTemplate, queryId++, keyLogName); + queriedLogsQueryMap.Add (keyLogName.ToLowerInvariant(), + query + "[" + providersPredicate); + } + } + else + { + List keysList = new List(queriedLogsQueryMap.Keys); + bool bRemovedIrrelevantLogs = false; + foreach (string queriedLog in keysList) + { + if (queriedLog.StartsWith(filePrefix, StringComparison.Ordinal)) + { + queriedLogsQueryMap[queriedLog] += "[" + BuildAllProvidersPredicate(); + } + else + { + if (_providersByLogMap.ContainsKey(queriedLog)) + { + string providersPredicate = BuildProvidersPredicate(_providersByLogMap[queriedLog]); + queriedLogsQueryMap[queriedLog] += "[" + providersPredicate; + } + else + { + WriteVerbose (string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("SpecifiedProvidersDontWriteToLog"), queriedLog)); + queriedLogsQueryMap.Remove(queriedLog); + bRemovedIrrelevantLogs = true; + } + } + } + // + // Write an error if we have removed all the logs as irrelevant + // + if (bRemovedIrrelevantLogs && (queriedLogsQueryMap.Count == 0)) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("LogsAndProvidersDontOverlap")); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "LogsAndProvidersDontOverlap", ErrorCategory.InvalidArgument, null)); + continue; + } + } + } + + // + // If none of the logs/paths/providers were valid, queriedLogsQueryMap is empty. + // Simply conitnue to the next hashtable since all the errors have been written already. + // + if (queriedLogsQueryMap.Count == 0) + { + continue; + } + + // + // At this point queriedLogsQueryMap contains all the query openings: missing the actual XPaths + // Let's build xpathString to attach to each query opening. + // + bool bDateTimeHandled = false; + foreach (string key in hash.Keys) + { + string added = ""; + + switch (key.ToLowerInvariant()) + { + case hashkey_logname_lc: + case hashkey_path_lc: + case hashkey_providername_lc: + break; + case hashkey_id_lc: + added = HandleEventIdHashValue(hash[key]); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + break; + + case hashkey_level_lc: + added = HandleLevelHashValue(hash[key]); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + break; + + case hashkey_keywords_lc: + added = HandleKeywordHashValue(hash[key]); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + break; + + case hashkey_starttime_lc: + if (bDateTimeHandled) + { + break; + } + added = HandleStartTimeHashValue(hash[key], hash); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + + bDateTimeHandled = true; + break; + + case hashkey_endtime_lc: + if (bDateTimeHandled) + { + break; + } + + added = HandleEndTimeHashValue(hash[key], hash); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + + bDateTimeHandled = true; + break; + + case hashkey_data_lc: + added = HandleDataHashValue(hash[key]); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + break; + + case hashkey_userid_lc: + added = HandleContextHashValue(hash[key]); + if (added.Length > 0) + { + ExtendPredicate(ref xpathString); + xpathString += added; + } + break; + + default: + { + // + // None of the recognized values: this must be a named payload field + // + ExtendPredicate(ref xpathString); + xpathString += string.Format(CultureInfo.InvariantCulture, + "([EventData[Data[@Name='{0}']='{1}']] or [UserData/*/{0}='{1}'])", + key, hash[key]); + } + break; + } + } + + // + // Complete each query with the XPath. + // Handle the case where the query opener already has provider predicate(s). + // Add the queries from queriedLogsQueryMap into the resulting string. + // + foreach (string query in queriedLogsQueryMap.Values) + { + result += query; + + if (query.EndsWith("*", StringComparison.OrdinalIgnoreCase)) + { + // + // No provider predicate: just add the XPath string + // + if (xpathString.Length != 0) + { + result += propOpen + xpathString + propClose; + } + } + else + { + // + // Add xpathString to provider predicates. + // + if (xpathString.Length != 0) + { + result += " and " + xpathString; + } + result += propClose; + } + + result += queryCloser; + + } + } //end foreach hashtable + + + result += queryListClose; + + return result; + } + + // + // HandleEventIdHashValue helper for hashtable structured query builder. + // Constructs and returns EventId XPath portion as a string. + // + private string HandleEventIdHashValue (Object value) + { + string ret = ""; + if (value is Array) + { + Array idsArray = (Array)(value); + ret += "("; + for (int i = 0; i < idsArray.Length; i++) + { + ret += "(System/EventID=" + idsArray.GetValue(i).ToString() + ")"; + if ( i < (idsArray.Length - 1)) + { + ret += " or "; + } + } + ret += ")"; + } + else + { + ret += "(System/EventID=" + value + ")"; + } + + return ret; + } + + // + // HandleLevelHashValue helper for hashtable structured query builder. + // Constructs and returns Level XPath portion as a string. + // + private string HandleLevelHashValue(Object value) + { + string ret = ""; + + if (value is Array) + { + Array levelsArray = (Array)(value); + ret += "("; + for (int i = 0; i < levelsArray.Length; i++) + { + ret += "(System/Level=" + levelsArray.GetValue(i).ToString() + ")"; + if ( i < (levelsArray.Length - 1)) + { + ret += " or "; + } + } + ret += ")"; + } + else + { + ret += "(System/Level=" + value + ")"; + } + + return ret; + } + + // + // HandleKeywordHashValue helper for hashtable structured query builder. + // Constructs and returns Keyword XPath portion as a string. + // + private string HandleKeywordHashValue(Object value) + { + Int64 keywordsMask = 0; + Int64 keywordLong = 0; + + if (value is Array) + { + foreach (Object keyword in (Array) value) + { + if (KeywordStringToInt64(keyword.ToString(), ref keywordLong)) + { + keywordsMask |= keywordLong; + } + } + } + else + { + if ( !KeywordStringToInt64(value.ToString(), ref keywordLong)) + { + return ""; + } + keywordsMask |= keywordLong; + } + + return string.Format(CultureInfo.InvariantCulture, "System[band(Keywords,{0})]",keywordsMask); + } + + // + // HandleContextHashValue helper for hashtable structured query builder. + // Constructs and returns UserID XPath portion as a string. + // Handles both SIDs and domain account names. + // Writes an error and returns an empty string if the SID or account names are not valid. + // + private string HandleContextHashValue(Object value) + { + + SecurityIdentifier sidCandidate = null; + try + { + sidCandidate = new SecurityIdentifier(value.ToString()); + } + catch(ArgumentException) + { + WriteDebug(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("InvalidSIDFormat"), value)); + } + + if (sidCandidate == null) + { + try + { + NTAccount acct = new NTAccount(value.ToString()); + sidCandidate = (SecurityIdentifier)acct.Translate(typeof(SecurityIdentifier)); + + } + catch(ArgumentException exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("InvalidContext"), value.ToString()); + Exception outerExc = new Exception (msg, exc); + WriteError(new ErrorRecord(outerExc, "InvalidContext", ErrorCategory.InvalidArgument, null)); + return ""; + } + } + + return string.Format(CultureInfo.InvariantCulture, "(System/Security[@UserID='{0}'])", sidCandidate.ToString()); + } + + + // + // HandleStartTimeHashValue helper for hashtable structured query builder. + // Constructs and returns TimeCreated XPath portion as a string. + // NOTE that it also handles the hashtable "endtime" value (if supplied). + // + private string HandleStartTimeHashValue(Object value, Hashtable hash) + { + string ret = ""; + + DateTime startTime = new DateTime(); + if (!StringToDateTime(value.ToString(), ref startTime)) + { + return ""; + } + + startTime = startTime.ToUniversalTime(); + string startTimeFormatted = startTime.ToString("s", CultureInfo.InvariantCulture) + "." + startTime.Millisecond.ToString("d3", CultureInfo.InvariantCulture) + "Z"; + + if (hash.ContainsKey(hashkey_endtime_lc)) + { + DateTime endTime = new DateTime(); + if (!StringToDateTime(hash[hashkey_endtime_lc].ToString(), ref endTime)) + { + return ""; + } + + endTime = endTime.ToUniversalTime(); + string endTimeFormatted = endTime.ToString("s", CultureInfo.InvariantCulture) + "." + endTime.Millisecond.ToString("d3", CultureInfo.InvariantCulture) + "Z"; + + ret += string.Format(CultureInfo.InvariantCulture, + "(System/TimeCreated[@SystemTime>='{0}' and @SystemTime<='{1}'])", + startTimeFormatted, endTimeFormatted); + } + else + { + ret += string.Format(CultureInfo.InvariantCulture, + "(System/TimeCreated[@SystemTime>='{0}'])", + startTimeFormatted); + } + + return ret; + } + + + // + // HandleEndTimeHashValue helper for hashtable structured query builder. + // Constructs and returns TimeCreated XPath portion as a string. + // NOTE that it also handles the hashtable "starttime" value (if supplied). + // + private string HandleEndTimeHashValue(Object value, Hashtable hash) + { + string ret = ""; + + DateTime endTime = new DateTime(); + if (!StringToDateTime(value.ToString(), ref endTime)) + { + return ""; + } + + endTime = endTime.ToUniversalTime(); + string endTimeFormatted = endTime.ToString("s", CultureInfo.InvariantCulture) + "." + + endTime.Millisecond.ToString("d3", CultureInfo.InvariantCulture) + "Z"; + + if (hash.ContainsKey(hashkey_starttime_lc)) + { + DateTime startTime = new DateTime(); + if (!StringToDateTime(hash[hashkey_starttime_lc].ToString(), ref startTime)) + { + return ""; + } + + startTime = startTime.ToUniversalTime(); + string startTimeFormatted = startTime.ToString("s", CultureInfo.InvariantCulture) + "." + + startTime.Millisecond.ToString("d3", CultureInfo.InvariantCulture) + "Z"; + + ret += string.Format(CultureInfo.InvariantCulture, "(System/TimeCreated[@SystemTime>='{0}' and @SystemTime<='{1}'])", + startTimeFormatted, endTimeFormatted); + } + else + { + ret += string.Format(CultureInfo.InvariantCulture, "(System/TimeCreated[@SystemTime<='{0}'])", + endTimeFormatted); + } + + return ret; + } + + // + // HandleDataHashValue helper for hashtable structured query builder. + // Constructs and returns EventData/Data XPath portion as a string. + // + private string HandleDataHashValue(Object value) + { + string ret = ""; + if (value is Array) + { + Array dataArray = (Array)(value); + ret += "("; + for (int i = 0; i < dataArray.Length; i++) + { + ret += string.Format(CultureInfo.InvariantCulture, "(EventData/Data='{0}')", dataArray.GetValue(i).ToString()); + if ( i < (dataArray.Length - 1)) + { + ret += " or "; + } + } + ret += ")"; + } + else + { + ret += string.Format(CultureInfo.InvariantCulture, "(EventData/Data='{0}')", value); + } + + return ret; + } + + + // + // Helper checking whether at least one of log, _path, provider is specified. + // It will ThrowTerminatingError in case none of those keys are present. + // + private void CheckHashTableForQueryPathPresence(Hashtable hash) + { + bool isLogHash = (hash.ContainsKey(hashkey_logname_lc)); + bool isPathHash = (hash.ContainsKey(hashkey_path_lc)); + bool isProviderHash = (hash.ContainsKey(hashkey_providername_lc)); + + if (!isLogHash && !isProviderHash && !isPathHash) + { + string msg = _resourceMgr.GetString("LogProviderOrPathNeeded"); + Exception exc = new Exception(msg); + ThrowTerminatingError(new ErrorRecord(exc, "LogProviderOrPathNeeded", ErrorCategory.InvalidArgument, null)); + } + } + + // + // TerminateForNonEvtxFileWithoutOldest terminates for .evt and .etl files unless -Oldest is specified. + // + private void TerminateForNonEvtxFileWithoutOldest (string fileName) + { + if (!Oldest.IsPresent) + { + if (System.IO.Path.GetExtension(fileName).Equals(".etl", StringComparison.OrdinalIgnoreCase) || + System.IO.Path.GetExtension(fileName).Equals(".evt", StringComparison.OrdinalIgnoreCase)) + { + string msg = _resourceMgr.GetString("SpecifyOldestForEtlEvt"); + Exception exc = new Exception (string.Format(CultureInfo.InvariantCulture, msg, fileName)); + ThrowTerminatingError (new ErrorRecord(exc, "SpecifyOldestForEtlEvt", ErrorCategory.InvalidArgument, fileName)); + } + } + } + + // + // ValidateLogName writes an error if logName is not a valid log. + // It also terminates for direct ETW channels unless -Oldest is specified. + // + private bool ValidateLogName (string logName, EventLogSession eventLogSession) + { + + EventLogConfiguration logObj; + try + { + logObj = new EventLogConfiguration(logName, eventLogSession); + } + catch (EventLogNotFoundException) + { + string msg = _resourceMgr.GetString("NoMatchingLogsFound"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, this._computerName, logName)); + WriteError (new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, logName)); + return false; + } + catch (Exception exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("LogInfoUnavailable"), + logName, exc.Message); + Exception outerExc = new Exception (msg, exc); + WriteError(new ErrorRecord( outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); + return false; + } + if (!Oldest.IsPresent) + { + if (logObj.LogType == EventLogType.Debug || logObj.LogType == EventLogType.Analytical) + { + string msg = _resourceMgr.GetString("SpecifyOldestForLog"); + Exception exc = new Exception (string.Format(CultureInfo.InvariantCulture, msg, logName)); + ThrowTerminatingError (new ErrorRecord(exc, "SpecifyOldestForLog", ErrorCategory.InvalidArgument, logName)); + } + } + return true; + } + + // + // ExtendPredicate helper for the query builder. + // Extends the XPath predicate string. + // + private void ExtendPredicate(ref string xpathString) + { + if (xpathString.Length != 0) + { + xpathString += " and "; + } + } + + + // + // KeywordStringToInt64 helper converts a string to Int64. + // Returns true and keyLong ref if successful. + // Writes an error and returns false if keyString cannot be converted. + // + private bool KeywordStringToInt64(string keyString, ref Int64 keyLong) + { + try + { + keyLong = Convert.ToInt64(keyString, CultureInfo.InvariantCulture); + + } + catch (Exception exc) + { + string msg = _resourceMgr.GetString("KeywordLongExpected"); + Exception outerExc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, keyString), exc); + WriteError(new ErrorRecord(outerExc, "KeywordLongExpected", ErrorCategory.InvalidArgument, null)); + return false; + } + + return true; + } + + // + // StringToDateTime helper converts a string to DateTime object. + // Returns true and DateTime ref if successful. + // Writes an error and returns false if dtString cannot be converted. + // + private bool StringToDateTime(string dtString, ref DateTime dt) + { + try + { + dt = DateTime.Parse(dtString, CultureInfo.CurrentCulture); + } + catch (FormatException exc) + { + string msg = _resourceMgr.GetString("DateTimeExpected"); + Exception outerExc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, dtString), exc); + WriteError(new ErrorRecord(outerExc, "DateTimeExpected", ErrorCategory.InvalidArgument, null)); + return false; + } + + return true; + } + + // + // ValidateAndResolveFilePath helper. + // Returns a string collection of resolved file paths. + // Writes non-terminating errors for invalid paths + // and returns an empty colleciton. + // + StringCollection ValidateAndResolveFilePath (string path) + { + + StringCollection retColl = new StringCollection(); + + Collection resolvedPathSubset = null; + try + { + resolvedPathSubset = SessionState.Path.GetResolvedPSPathFromPSPath(path); + } + catch (PSNotSupportedException notSupported) + { + WriteError(new ErrorRecord(notSupported, "", ErrorCategory.ObjectNotFound, path)); + return retColl; + } + catch (System.Management.Automation.DriveNotFoundException driveNotFound) + { + WriteError(new ErrorRecord(driveNotFound, "", ErrorCategory.ObjectNotFound, path)); + return retColl; + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError(new ErrorRecord(providerNotFound, "", ErrorCategory.ObjectNotFound, path)); + return retColl; + } + catch (ItemNotFoundException pathNotFound) + { + WriteError(new ErrorRecord(pathNotFound, "", ErrorCategory.ObjectNotFound, path)); + return retColl; + } + catch(Exception exc) + { + WriteError(new ErrorRecord(exc, "", ErrorCategory.ObjectNotFound, path)); + return retColl; + } + + foreach (PathInfo pi in resolvedPathSubset) + { + // + // Check the provider: only FileSystem provider paths are acceptable. + // + if (pi.Provider.Name != "FileSystem") + { + string msg = _resourceMgr.GetString("NotAFileSystemPath"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, path)); + WriteError(new ErrorRecord(exc, "NotAFileSystemPath", ErrorCategory.InvalidArgument, path)); + continue; + } + + // + // Check the extension: only .evt, .evtx, and .etl files are allowed. + // If the file was specified without wildcards, display an error. + // Otherwise, skip silently. + // + if (!System.IO.Path.GetExtension(pi.Path).Equals(".evt", StringComparison.OrdinalIgnoreCase ) && + !System.IO.Path.GetExtension(pi.Path).Equals(".evtx", StringComparison.OrdinalIgnoreCase ) && + !System.IO.Path.GetExtension(pi.Path).Equals(".etl", StringComparison.OrdinalIgnoreCase )) + { + if (!WildcardPattern.ContainsWildcardCharacters(path)) + { + string msg = _resourceMgr.GetString("NotALogFile"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, pi.ProviderPath)); + WriteError(new ErrorRecord(exc, "NotALogFile", ErrorCategory.InvalidArgument, path)); + } + continue; + } + + TerminateForNonEvtxFileWithoutOldest (pi.ProviderPath); + + retColl.Add(pi.ProviderPath.ToLowerInvariant()); + + } + + return retColl; + } + + // + // CheckHashTablesForNullValues() checks all _selector values + // and writes a terminating error when it encounters a null + // as a single value or as part of an array. + // + private void CheckHashTablesForNullValues() + { + foreach (Hashtable hash in _selector) + { + foreach (string key in hash.Keys) + { + Object value = hash[key]; + if (value == null) + { + string msg = _resourceMgr.GetString("NullNotAllowedInHashtable"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); + ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); + } + else if (value is Array) + { + foreach (Object elt in (Array)value) + { + if (elt == null) + { + string msg = _resourceMgr.GetString("NullNotAllowedInHashtable"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); + ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); + } + } + } + } + } + } + + // + // AddProviderPredicatesToFilter() builds an XPath query + // by adding provider predicates to _filter. + // Note that this is by no means an XPath expression parser + // and will may produce garbage if the _filterXPath expression provided by the user is invalid. + // However, we are relying on the EventLog XPath parser to reject the garbage later on. + // + private string AddProviderPredicatesToFilter (StringCollection providers) + { + if (providers.Count == 0) + { + return _filter; + } + + string ret = _filter; + string predicate = BuildProvidersPredicate (providers); + + if (_filter.Equals("*", StringComparison.OrdinalIgnoreCase)) + { + ret += "[" + predicate + "]"; + } + else + { + // + // Extend the XPath provided in the _filter + // + int lastPredClose = _filter.LastIndexOf(']'); + if (lastPredClose == -1) + { + + ret += "[" + predicate + "]"; + } + else + { + ret = ret.Insert (lastPredClose, " and " + predicate); + } + } + + return ret; + } + + + // + // BuildProvidersPredicate() builds a predicate expression like: + // "System/Provider[@Name='a' or @Name='b']" + // for all provider names specified in the "providers" argument. + // + private string BuildProvidersPredicate(StringCollection providers) + { + if (providers.Count == 0) + { + return ""; + } + + string predicate = "System/Provider["; + for (int i = 0; i < providers.Count; i++) + { + predicate += "@Name='" + providers[i] + "'"; + if (i < (providers.Count - 1)) + { + predicate += " or "; + } + } + predicate += "]"; + + return predicate; + } + + + // + // BuildAllProvidersPredicate() builds a predicate expression like: + // "System/Provider[@Name='a' or @Name='b']" + // for all unique provider names specified in _providersByLogMap. + // Eliminates duplicates, too, since the same provider can + // be writing to several different logs. + // + private string BuildAllProvidersPredicate() + { + if (_providersByLogMap.Count == 0) + { + return ""; + } + + string predicate = "System/Provider["; + + List uniqueProviderNames = new List(); + + foreach (string logKey in _providersByLogMap.Keys) + { + for (int i = 0; i < _providersByLogMap[logKey].Count; i++) + { + string lowerCaseProviderName = _providersByLogMap[logKey][i].ToLowerInvariant(); + if (!uniqueProviderNames.Contains(lowerCaseProviderName)) + { + uniqueProviderNames.Add(lowerCaseProviderName); + } + } + } + + for (int i = 0; i < uniqueProviderNames.Count; i++) + { + predicate += "@Name='" + uniqueProviderNames[i] + "'"; + if (i < uniqueProviderNames.Count - 1) + { + predicate += " or "; + } + } + + predicate += "]"; + + return predicate; + } + + + // + // AddLogsForProviderToInternalMap helper. + // Retrieves log names to which _providerName writes. + // NOTE: there are many misconfigured providers in the system. + // We therefore catch EventLogException excpetions and write them out as non-terminating errors. + // The results are added to _providersByLogMap dictionary. + // + private void AddLogsForProviderToInternalMap(EventLogSession eventLogSession, string providerName) + { + + try + { + ProviderMetadata providerMetadata = new ProviderMetadata(providerName, eventLogSession, CultureInfo.CurrentCulture); + + System.Collections.IEnumerable logLinks = providerMetadata.LogLinks; + + foreach (EventLogLink logLink in logLinks) + { + + if (!_providersByLogMap.ContainsKey(logLink.LogName.ToLowerInvariant())) + { + // + // Skip direct ETW channels unless -force is present. + // Error out for direct channels unless -oldest is present. + // + EventLogConfiguration logObj = new EventLogConfiguration(logLink.LogName, eventLogSession); + if (logObj.LogType == EventLogType.Debug || logObj.LogType == EventLogType.Analytical) + { + if (!Force.IsPresent) + { + continue; + } + + ValidateLogName (logLink.LogName, eventLogSession); + } + + WriteVerbose(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderLogLink"), providerName, logLink.LogName)); + + StringCollection provColl = new StringCollection(); + provColl.Add(providerName.ToLowerInvariant()); + + _providersByLogMap.Add(logLink.LogName.ToLowerInvariant(), provColl); + } + else + + { + // + // Log is there: add provider, if needed + // + StringCollection coll = _providersByLogMap[logLink.LogName.ToLowerInvariant()]; + + if (!coll.Contains(providerName.ToLowerInvariant())) + { + WriteVerbose (string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderLogLink"), providerName, logLink.LogName)); + + coll.Add(providerName.ToLowerInvariant()); + } + } + } + } + catch (System.Diagnostics.Eventing.Reader.EventLogException exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("ProviderMetadataUnavailable"), + providerName, exc.Message); + Exception outerExc = new Exception (msg, exc); + WriteError(new ErrorRecord( outerExc, "ProviderMetadataUnavailable", ErrorCategory.NotSpecified, null)); + return; + } + } + + // + // FindLogNamesMatchingWildcards helper. + // Finds all logs whose names match wildcard patterns in the 'logPatterns' argument. + // For each non-matched pattern, a non-terminating error is written. + // The results are added to _logNamesMatchingWildcard array. + // + private void FindLogNamesMatchingWildcards (EventLogSession eventLogSession, IEnumerable logPatterns) + { + if (_logNamesMatchingWildcard == null) + { + _logNamesMatchingWildcard = new StringCollection(); + } + else + { + _logNamesMatchingWildcard.Clear(); + } + + foreach (string logPattern in logPatterns) + { + bool bMatched = false; + foreach (string actualLogName in eventLogSession.GetLogNames()) + { + WildcardPattern wildLogPattern = new WildcardPattern(logPattern, WildcardOptions.IgnoreCase); + + if (((!WildcardPattern.ContainsWildcardCharacters(logPattern)) + && (logPattern.Equals(actualLogName, StringComparison.CurrentCultureIgnoreCase))) + || + (wildLogPattern.IsMatch(actualLogName))) + { + // + // Skip direct ETW channels matching wildcards unless -force is present. + // Error out for direct channels unless -oldest is present. + // + EventLogConfiguration logObj; + try + { + logObj = new EventLogConfiguration(actualLogName, eventLogSession); + } + catch (Exception exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("LogInfoUnavailable"), + actualLogName, exc.Message); + Exception outerExc = new Exception (msg, exc); + WriteError(new ErrorRecord( outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); + continue; + } + + if (logObj.LogType == EventLogType.Debug || logObj.LogType == EventLogType.Analytical) + { + if (WildcardPattern.ContainsWildcardCharacters(logPattern) && !Force.IsPresent) + { + continue; + } + + ValidateLogName(actualLogName, eventLogSession); + } + + if (!_logNamesMatchingWildcard.Contains(actualLogName.ToLowerInvariant())) + { + _logNamesMatchingWildcard.Add(actualLogName.ToLowerInvariant()); + } + bMatched = true; + } + } + if (!bMatched) + { + string msg = _resourceMgr.GetString("NoMatchingLogsFound"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, this._computerName, logPattern)); + WriteError (new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, logPattern)); + } + } + + } + + // + // FindProvidersByLogForWildcardPatterns helper. + // Finds all providers whose names match wildcard patterns in 'providerPatterns' argument. + // For each non-matched pattern, a non-terminating error is written. + // The results are added to _providersByLogMap dictionary (keyed by log names to which these providers write). + // + private void FindProvidersByLogForWildcardPatterns(EventLogSession eventLogSession, IEnumerable providerPatterns) + { + _providersByLogMap.Clear(); + + foreach (string provPattern in providerPatterns) + { + bool bMatched = false; + foreach (string provName in eventLogSession.GetProviderNames()) + { + WildcardPattern wildProvPattern = new WildcardPattern(provPattern, WildcardOptions.IgnoreCase); + + if (((!WildcardPattern.ContainsWildcardCharacters(provPattern)) + && (provPattern.Equals(provName, StringComparison.CurrentCultureIgnoreCase))) + || + (wildProvPattern.IsMatch(provName))) + { + WriteVerbose (string.Format(CultureInfo.InvariantCulture, "Found matching provider: {0}", provName)); + AddLogsForProviderToInternalMap(eventLogSession, provName); + bMatched = true; + } + } + if (!bMatched) + { + string msg = _resourceMgr.GetString("NoMatchingProvidersFound"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, this._computerName, provPattern)); + WriteError (new ErrorRecord(exc, "NoMatchingProvidersFound", ErrorCategory.ObjectNotFound, provPattern)); + } + } + } + + } + +} + + diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs new file mode 100644 index 000000000..d73dcab27 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. +// + + +using System; +using System.Collections.Generic; +using System.Text; +using System.Management.Automation; +using System.ComponentModel; + +namespace Microsoft.PowerShell.Commands +{ + + /// + /// Create the PowerShell snap-in used to register the + /// Get-WinEvent cmdlet. Declaring the PSSnapIn class identifies + /// this .cs file as a PowerShell snap-in. + /// + [RunInstaller(true)] + public class GetEventPSSnapIn : PSSnapIn + { + /// + /// Create an instance of the GetEventPSSnapIn class. + /// + public GetEventPSSnapIn() + : base() + { + } + + /// + /// Specify the name of the PowerShell snap-in. + /// + public override string Name + { + get + { + return "Microsoft.Powershell.GetEvent"; + } + } + + /// + /// Specify the vendor of the PowerShell snap-in. + /// + public override string Vendor + { + get + { + return "Microsoft"; + } + } + + /// + /// Get resource information for vendor. This is a string of format: resourceBaseName,resourceName. + /// + public override string VendorResource + { + get + { + return "GetEventResources,Vendor"; + } + } + + /// + /// Specifies the description of the PowerShell snap-in. + /// + public override string Description + { + get + { + return "This PS snap-in contains Get-WinEvent cmdlet used to read Windows event log data and configuration."; + } + } + + /// + /// Get resource information for description. This is a string of format: resourceBaseName,resourceName. + /// + public override string DescriptionResource + { + get + { + return "GetEventResources,Description"; + } + } + + /// + /// Get type files to be used for this mshsnapin. + /// + public override string[] Types + { + get + { + return _types; + } + } + private string[] _types = new string[] {"getevent.types.ps1xml"}; + + /// + /// Get format files to be used for this mshsnapin. + /// + public override string[] Formats + { + get + { + return _formats; + } + } + + private string[] _formats = new string[] {"Event.format.ps1xml"}; + } +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs new file mode 100644 index 000000000..a54c5e7b5 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs @@ -0,0 +1,676 @@ +// +// Copyright (c) 2008 Microsoft Corporation. All rights reserved. +// + + +using System; +using System.Text; +using System.IO; +using System.Xml; +using System.Net; +using System.Management.Automation; +using System.ComponentModel; +using System.Reflection; +using System.Globalization; +using System.Management.Automation.Runspaces; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Security; +using System.Security.Principal; +using System.Resources; +using System.Threading; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Powershell.Commands.GetCounter.PdhNative; +using Microsoft.PowerShell.Commands.GetCounter; +using Microsoft.PowerShell.Commands.Diagnostics.Common; + + +namespace Microsoft.PowerShell.Commands +{ + + /// + /// Class that implements the Get-Counter cmdlet. + /// + [Cmdlet("Import", "Counter", DefaultParameterSetName = "GetCounterSet", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=138338")] + public sealed class ImportCounterCommand : PSCmdlet + { + // + // Path parameter + // + [Parameter( + Position = 0, + Mandatory = true, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true, + HelpMessageBaseName = "GetEventResources")] + [Alias("PSPath")] + + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ListSet", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] Path + { + get {return _path;} + set {_path = value;} + } + private string[] _path; + + private StringCollection _resolvedPaths = new StringCollection(); + + private List _accumulatedFileNames = new List(); + + + // + // ListSet parameter + // + [Parameter( + Mandatory = true, + ParameterSetName="ListSetSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [AllowEmptyCollection] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ListSet", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] ListSet + { + get {return _listSet;} + set {_listSet = value;} + } + private string[] _listSet = new string[0]; + + + // + // StartTime parameter + // + [Parameter( + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + ParameterSetName="GetCounterSet", + HelpMessageBaseName = "GetEventResources")] + public DateTime StartTime + { + get {return _startTime;} + set {_startTime = value;} + } + private DateTime _startTime = DateTime.MinValue; + + + // + // EndTime parameter + // + [Parameter( + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + ParameterSetName="GetCounterSet", + HelpMessageBaseName = "GetEventResources")] + public DateTime EndTime + { + get {return _endTime;} + set {_endTime = value;} + } + private DateTime _endTime = DateTime.MaxValue; + + + // + // Counter parameter + // + [Parameter( + Mandatory = false, + ParameterSetName="GetCounterSet", + ValueFromPipeline = false, + HelpMessageBaseName = "GetEventResources")] + [AllowEmptyCollection] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Scope = "member", + Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ListSet", + Justification="A string[] is required here because that is the type Powershell supports")] + public string[] Counter + { + get {return _counter;} + set {_counter = value;} + } + private string[] _counter = new string[0]; + + // + // Summary switch + // + [Parameter(ParameterSetName="SummarySet")] + public SwitchParameter Summary + { + get {return _summary;} + set{_summary = value;} + } + private SwitchParameter _summary; + + // + // MaxSamples parameter + // + private const Int64 KEEP_ON_SAMPLING = -1; + [Parameter( + ParameterSetName="GetCounterSet", + ValueFromPipeline = false, + ValueFromPipelineByPropertyName = false, + HelpMessageBaseName = "GetEventResources")] + [ValidateRange((Int64)1, Int64.MaxValue)] + public Int64 MaxSamples + { + get {return _maxSamples;} + set {_maxSamples = value;} + } + private Int64 _maxSamples = KEEP_ON_SAMPLING; + + + private ResourceManager _resourceMgr = null; + + private PdhHelper _pdhHelper = null; + + private bool _stopping = false; + + + // + // AccumulatePipelineFileNames() accumulates counter file paths in the pipeline scenario: + // we do not want to construct a Pdh query until all the file names are supplied. + // + private void AccumulatePipelineFileNames() + { + _accumulatedFileNames.AddRange(_path); + } + + + // + // BeginProcessing() is invoked once per pipeline + // + protected override void BeginProcessing() + { + _resourceMgr = new ResourceManager("GetEventResources", Assembly.GetExecutingAssembly()); + _pdhHelper = new PdhHelper(System.Environment.OSVersion.Version.Major < 6); + } + + // + // EndProcessing() is invoked once per pipeline + // + protected override void EndProcessing() + { + + // + // Resolve and validate the Path argument: present for all parametersets. + // + if (!ResolveFilePaths ()) + { + return; + } + ValidateFilePaths(); + + switch (ParameterSetName) + { + case "ListSetSet": + ProcessListSet(); + break; + + case "GetCounterSet": + ProcessGetCounter(); + break; + + case "SummarySet": + ProcessSummary(); + break; + + default: + Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + break; + } + + _pdhHelper.Dispose(); + } + + + // + // Handle Control-C + // + protected override void StopProcessing() + { + _stopping = true; + _pdhHelper.Dispose(); + } + + + // + // ProcessRecord() override. + // This is the main entry point for the cmdlet. + // + protected override void ProcessRecord() + { + + AccumulatePipelineFileNames(); + + } + + // + // ProcessSummary(). + // Does the work to process Summary parameter set. + // + private void ProcessSummary() + { + uint res = _pdhHelper.ConnectToDataSource(_resolvedPaths); + if (res != 0) + { + ReportPdhError(res, true); + return; + } + + CounterFileInfo summaryObj; + res = _pdhHelper.GetFilesSummary (out summaryObj); + + if (res != 0) + { + ReportPdhError(res, true); + return; + } + + WriteObject(summaryObj); + } + + // + // ProcessListSet(). + // Does the work to process ListSet parameter set. + // + private void ProcessListSet() + { + + uint res = _pdhHelper.ConnectToDataSource(_resolvedPaths); + if (res != 0) + { + ReportPdhError(res, true); + return; + } + + StringCollection machineNames = new StringCollection(); + res = _pdhHelper.EnumBlgFilesMachines(ref machineNames); + if (res != 0) + { + ReportPdhError(res, true); + return; + } + + foreach (string machine in machineNames) + { + StringCollection counterSets = new StringCollection(); + res = _pdhHelper.EnumObjects(machine, ref counterSets); + if (res != 0) + { + return; + } + + StringCollection validPaths = new StringCollection(); + + foreach (string pattern in _listSet) + { + bool bMatched = false; + + WildcardPattern wildLogPattern = new WildcardPattern(pattern, WildcardOptions.IgnoreCase); + + foreach (string counterSet in counterSets) + { + if (!wildLogPattern.IsMatch(counterSet)) + { + continue; + } + + + StringCollection counterSetCounters = new StringCollection(); + StringCollection counterSetInstances = new StringCollection(); + + res = _pdhHelper.EnumObjectItems(machine, counterSet, ref counterSetCounters, ref counterSetInstances); + if (res != 0) + { + ReportPdhError(res, false); + continue; + } + + string[] instanceArray = new string[counterSetInstances.Count]; + int i = 0; + foreach (string instance in counterSetInstances) + { + instanceArray[i++] = instance; + } + + Dictionary counterInstanceMapping = new Dictionary(); + foreach (string counter in counterSetCounters) + { + counterInstanceMapping.Add(counter, instanceArray); + } + + PerformanceCounterCategoryType categoryType = PerformanceCounterCategoryType.Unknown; + if (counterSetInstances.Count > 1) + { + categoryType = PerformanceCounterCategoryType.MultiInstance; + } + else //if (counterSetInstances.Count == 1) //??? + { + categoryType = PerformanceCounterCategoryType.SingleInstance; + } + + string setHelp = _pdhHelper.GetCounterSetHelp(machine, counterSet); + + CounterSet setObj = new CounterSet(counterSet, machine, categoryType, setHelp, ref counterInstanceMapping); + WriteObject (setObj); + bMatched = true; + } + if (!bMatched) + { + string msg = _resourceMgr.GetString("NoMatchingCounterSetsInFile"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, + CommonUtilities.StringArrayToString(_resolvedPaths), + pattern)); + WriteError (new ErrorRecord(exc, "NoMatchingCounterSetsInFile", ErrorCategory.ObjectNotFound, null)); + } + } + } + + + + } + + // + // ProcessGetCounter() + // Does the work to process GetCounterSet parameter set. + // + private void ProcessGetCounter() + { + // Validate StartTime-EndTime, if present + if (_startTime != DateTime.MinValue || _endTime != DateTime.MaxValue) + { + if (_startTime >= _endTime) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterInvalidDateRange")); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "CounterInvalidDateRange", ErrorCategory.InvalidArgument, null)); + return; + } + } + + uint res = _pdhHelper.ConnectToDataSource(_resolvedPaths); + if (res != 0) + { + ReportPdhError(res, true); + return; + } + + StringCollection validPaths = new StringCollection(); + if (_counter.Length > 0) + { + foreach (string path in _counter) + { + StringCollection expandedPaths; + res = _pdhHelper.ExpandWildCardPath(path, out expandedPaths); + if (res != 0) + { + WriteDebug(path); + ReportPdhError(res, false); + continue; + } + + foreach (string expandedPath in expandedPaths) + { + if (!_pdhHelper.IsPathValid(expandedPath)) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterPathIsInvalid"), path); + Exception exc = new Exception (msg); + WriteError(new ErrorRecord(exc, "CounterPathIsInvalid", ErrorCategory.InvalidResult, null)); + + continue; + } + validPaths.Add(expandedPath); + } + } + if (validPaths.Count == 0) + { + return; + } + } + else + { + res = _pdhHelper.GetValidPathsFromFiles(ref validPaths); + if (res != 0) + { + ReportPdhError(res, false); + } + } + + if (validPaths.Count == 0) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterPathsInFilesInvalid")); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "CounterPathsInFilesInvalid", ErrorCategory.InvalidResult, null)); + } + + res = _pdhHelper.OpenQuery(); + if (res != 0) + { + ReportPdhError(res, false); + } + + if (_startTime != DateTime.MinValue || _endTime != DateTime.MaxValue) + { + res = _pdhHelper.SetQueryTimeRange(_startTime, _endTime); + if (res != 0) + { + ReportPdhError(res, true); + } + } + + res = _pdhHelper.AddCounters(ref validPaths, true); + if (res != 0) + { + ReportPdhError(res, true); + } + + PerformanceCounterSampleSet nextSet; + + uint samplesRead = 0; + + while (!_stopping) + { + res = _pdhHelper.ReadNextSet (out nextSet, false); + if (res == PdhResults.PDH_NO_MORE_DATA) + { + break; + } + if (res != 0 && res != PdhResults.PDH_INVALID_DATA) + { + ReportPdhError(res, false); + continue; + } + + // + // Display data + // + WriteSampleSetObject (nextSet, (samplesRead == 0)); + + samplesRead++; + + if (_maxSamples != KEEP_ON_SAMPLING && samplesRead >= _maxSamples) + { + break; + } + } + } + + + // + // ValidateFilePaths() helper. + // Validates the _resolvedPaths: present for all parametersets. + // We cannot have more than 32 blg files, or more than one CSV or TSC file. + // Files have to all be of the same type (.blg, .csv, .tsv). + // + private void ValidateFilePaths() + { + Debug.Assert(_resolvedPaths.Count > 0); + + string firstExt = System.IO.Path.GetExtension(_resolvedPaths[0]); + foreach (string fileName in _resolvedPaths) + { + + WriteVerbose(fileName); + string curExtension = System.IO.Path.GetExtension(fileName); + + if (!curExtension.Equals(".blg", StringComparison.CurrentCultureIgnoreCase) + && !curExtension.Equals(".csv", StringComparison.CurrentCultureIgnoreCase) + && !curExtension.Equals(".tsv", StringComparison.CurrentCultureIgnoreCase)) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterNotALogFile"), fileName); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "CounterNotALogFile", ErrorCategory.InvalidResult, null)); + return; + } + + if (!curExtension.Equals(firstExt, StringComparison.CurrentCultureIgnoreCase)) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterNoMixedLogTypes"), fileName); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "CounterNoMixedLogTypes", ErrorCategory.InvalidResult, null)); + return; + } + } + + if (firstExt.Equals(".blg", StringComparison.CurrentCultureIgnoreCase)) + { + if (_resolvedPaths.Count > 32) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("Counter32FileLimit")); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "Counter32FileLimit", ErrorCategory.InvalidResult, null)); + return; + } + } + else if (_resolvedPaths.Count > 1) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("Counter1FileLimit")); + Exception exc = new Exception (msg); + ThrowTerminatingError(new ErrorRecord(exc, "Counter1FileLimit", ErrorCategory.InvalidResult, null)); + return; + } + } + + + // + // ResolveFilePath helper. + // Returns a string collection of resolved file paths. + // Writes non-terminating errors for invalid paths + // and returns an empty colleciton. + // + bool ResolveFilePaths() + { + + StringCollection retColl = new StringCollection(); + + foreach (string origPath in _accumulatedFileNames) + { + + Collection resolvedPathSubset = null; + try + { + resolvedPathSubset = SessionState.Path.GetResolvedPSPathFromPSPath(origPath); + } + catch (PSNotSupportedException notSupported) + { + WriteError(new ErrorRecord(notSupported, "", ErrorCategory.ObjectNotFound, origPath)); + continue; + } + catch (System.Management.Automation.DriveNotFoundException driveNotFound) + { + WriteError(new ErrorRecord(driveNotFound, "", ErrorCategory.ObjectNotFound, origPath)); + continue; + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError(new ErrorRecord(providerNotFound, "", ErrorCategory.ObjectNotFound, origPath)); + continue; + } + catch (ItemNotFoundException pathNotFound) + { + WriteError(new ErrorRecord(pathNotFound, "", ErrorCategory.ObjectNotFound, origPath)); + continue; + } + catch(Exception exc) + { + WriteError(new ErrorRecord(exc, "", ErrorCategory.ObjectNotFound, origPath)); + continue; + } + + foreach (PathInfo pi in resolvedPathSubset) + { + // + // Check the provider: only FileSystem provider paths are acceptable. + // + if (pi.Provider.Name != "FileSystem") + { + string msg = _resourceMgr.GetString("NotAFileSystemPath"); + Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, origPath)); + WriteError(new ErrorRecord(exc, "NotAFileSystemPath", ErrorCategory.InvalidArgument, origPath)); + continue; + } + + _resolvedPaths.Add(pi.ProviderPath.ToLower(CultureInfo.InvariantCulture)); + } + } + + return (_resolvedPaths.Count > 0); + } + + private void ReportPdhError(uint res, bool bTerminate) + { + string msg; + uint formatRes = CommonUtilities.FormatMessageFromModule(res, "pdh.dll", out msg); + if (formatRes != 0) + { + msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res); + } + Exception exc = new Exception (msg); + if (bTerminate) + { + ThrowTerminatingError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + else + { + WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + } + } + + // + // WriteSampleSetObject() helper. + // In addition to writing the PerformanceCounterSampleSet object, + // it writes a single error if one of the samples has an invalid (non-zero) status. + // The only exception is the first set, where we allow for the formatted value to be 0 - + // this is expected for CSV and TSV files. + + private void WriteSampleSetObject(PerformanceCounterSampleSet set, bool firstSet) + { + if (!firstSet) + { + foreach (PerformanceCounterSample sample in set.CounterSamples) + { + if (sample.Status != 0) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterSampleDataInvalid")); + Exception exc = new Exception(msg); + WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); + break; + } + } + } + + WriteObject (set); + } + + } +} + + diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs new file mode 100644 index 000000000..7c4326540 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs @@ -0,0 +1,352 @@ +// +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. +// + +using System; +using System.Management.Automation; +using System.Globalization; +using System.Reflection; +using System.Diagnostics.Eventing; +using System.Diagnostics.Eventing.Reader; +using System.Resources; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Xml; +using System.IO; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Class that implements the New-WinEvent cmdlet. + /// This cmdlet writes a new Etw event using the provider specified in parameter. + /// + [Cmdlet(VerbsCommon.New, "WinEvent", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217469")] + public sealed class NewWinEventCommand : PSCmdlet + { + private ProviderMetadata providerMetadata; + private EventDescriptor? eventDescriptor; + + private const string TemplateTag = "template"; + private const string DataTag = "data"; + private ResourceManager _resourceMgr = new ResourceManager("GetEventResources", typeof(NewWinEventCommand).GetTypeInfo().Assembly); + + + /// + /// ProviderName + /// + [Parameter( + Position = 0, + Mandatory = true, + ParameterSetName = ParameterAttribute.AllParameterSets)] + public string ProviderName + { + get + { + return providerName; + } + set + { + providerName = value; + } + } + private string providerName; + + /// + /// Id (EventId defined in manifest file) + /// + [Parameter( + Position = 1, + Mandatory = true, + ParameterSetName = ParameterAttribute.AllParameterSets)] + public int Id + { + get + { + return id; + } + set + { + id = value; + idSpecified = true; + } + } + private int id; + private bool idSpecified = false; + + + /// + /// Version (event version) + /// + [Parameter( + Mandatory = false, + ParameterSetName = ParameterAttribute.AllParameterSets)] + public byte Version + { + get + { + return version; + } + set + { + version = value; + versionSpecified = true; + } + } + private byte version; + private bool versionSpecified = false; + + + /// + /// Event Payload + /// + [Parameter( + Position = 2, + Mandatory = false, + ParameterSetName = ParameterAttribute.AllParameterSets), + AllowEmptyCollection, + SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", + Target = "Microsoft.PowerShell.Commands", + Justification="A string[] is required here because that is the type Powershell supports")] + public object[] Payload + { + get + { + return payload; + } + set + { + payload = value; + } + } + private object[] payload; + + /// + /// BeginProcessing + /// + protected override void BeginProcessing() + { + LoadProvider(); + LoadEventDescriptor(); + + base.BeginProcessing(); + } + + private void LoadProvider() + { + if (string.IsNullOrEmpty(this.providerName)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderNotSpecified")), "ProviderName"); + } + + using (EventLogSession session = new EventLogSession()) + { + foreach (string providerName in session.GetProviderNames()) + { + if (string.Equals(providerName, this.providerName, StringComparison.OrdinalIgnoreCase)) + { + try + { + this.providerMetadata = new ProviderMetadata(providerName); + } + catch (EventLogException exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderMetadataUnavailable"), providerName, exc.Message); + throw new Exception(msg, exc); + } + break; + } + } + } + + if (this.providerMetadata == null) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoProviderFound"), providerName); + throw new ArgumentException(msg); + } + } + + private void LoadEventDescriptor() + { + if (idSpecified) + { + List matchedEvents = new List(); + foreach (EventMetadata emd in this.providerMetadata.Events) + { + if (emd.Id == this.id) + { + matchedEvents.Add(emd); + } + } + + if (matchedEvents.Count == 0) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("IncorrectEventId"), + this.id, + this.providerName); + throw new EventWriteException(msg); + } + + EventMetadata matchedEvent = null; + if (!versionSpecified && matchedEvents.Count == 1) + { + matchedEvent = matchedEvents[0]; + } + else + { + if (versionSpecified) + { + foreach (EventMetadata emd in matchedEvents) + { + if (emd.Version == this.version) + { + matchedEvent = emd; + break; + } + } + if (matchedEvent == null) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("IncorrectEventVersion"), + this.version, + this.id, + this.providerName); + + throw new EventWriteException(msg); + } + } + else + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("VersionNotSpecified"), + this.id, + this.providerName); + + throw new EventWriteException(msg); + } + } + + VerifyTemplate(matchedEvent); + this.eventDescriptor = CreateEventDescriptor(this.providerMetadata, matchedEvent); + } + else + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("EventIdNotSpecified")), "Id"); + } + } + + private bool VerifyTemplate(EventMetadata emd) + { + if (emd.Template != null) + { + XmlReaderSettings readerSettings = new XmlReaderSettings + { + CheckCharacters = false, + IgnoreComments = true, + IgnoreProcessingInstructions = true, + MaxCharactersInDocument = 0, // no limit + ConformanceLevel = ConformanceLevel.Fragment, +#if !CORECLR + XmlResolver = null, +#endif + }; + + int definedParameterCount = 0; + using (XmlReader reader = XmlReader.Create(new StringReader(emd.Template), readerSettings)) + { + if (reader.ReadToFollowing(TemplateTag)) + { + bool found = reader.ReadToDescendant(DataTag); + while (found) + { + definedParameterCount++; + found = reader.ReadToFollowing(DataTag); + } + } + } + + if ((this.payload == null && definedParameterCount != 0) + || ((this.payload != null) && this.payload.Length != definedParameterCount)) + { + string warning = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("PayloadMismatch"), this.id, emd.Template); + WriteWarning(warning); + + return false; + } + } + return true; + } + + private static EventDescriptor CreateEventDescriptor(ProviderMetadata providerMetaData, EventMetadata emd) + { + long keywords = 0; + foreach (EventKeyword keyword in emd.Keywords) + { + keywords |= keyword.Value; + } + + byte channel = 0; + foreach (EventLogLink logLink in providerMetaData.LogLinks) + { + if (string.Equals(logLink.LogName, emd.LogLink.LogName, StringComparison.OrdinalIgnoreCase)) + break; + channel++; + } + + return new EventDescriptor( + (int) emd.Id, + emd.Version, + channel, + (byte) emd.Level.Value, + (byte) emd.Opcode.Value, + emd.Task.Value, + keywords); + } + + /// + /// ProcessRecord + /// + protected override void ProcessRecord() + { + using (EventProvider provider = new EventProvider(this.providerMetadata.Id)) + { + EventDescriptor ed = this.eventDescriptor.Value; + + if (this.payload != null && this.payload.Length > 0) + { + for (int i = 0; i < this.payload.Length; i++) + { + if (this.payload[i] == null) + { + this.payload[i] = string.Empty; + } + } + provider.WriteEvent(ref ed, this.payload); + } else { + provider.WriteEvent(ref ed); + } + } + base.ProcessRecord(); + } + + /// + /// EndProcessing + /// + protected override void EndProcessing() + { + if (this.providerMetadata != null) + providerMetadata.Dispose(); + + base.EndProcessing(); + } + } + + internal class EventWriteException : Exception + { + internal EventWriteException(string msg, Exception innerException) + : base (msg, innerException) {} + + internal EventWriteException(string msg) + : base (msg) {} + } +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs new file mode 100644 index 000000000..208932dc6 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs @@ -0,0 +1,1796 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Runtime.InteropServices.ComTypes; +using Microsoft.PowerShell.Commands.GetCounter; +using Microsoft.Win32; + +namespace Microsoft.Powershell.Commands.GetCounter.PdhNative +{ + + internal static class PdhResults + { + public const long PDH_CSTATUS_VALID_DATA = 0x0L; + public const long PDH_CSTATUS_NEW_DATA = 0x1L; + public const long PDH_CSTATUS_NO_MACHINE = 0x800007D0L; + public const long PDH_CSTATUS_NO_INSTANCE = 0x800007D1L; + public const long PDH_MORE_DATA = 0x800007D2L; + public const long PDH_CSTATUS_ITEM_NOT_VALIDATED = 0x800007D3L; + public const long PDH_RETRY = 0x800007D4L; + public const long PDH_NO_DATA = 0x800007D5L; + public const long PDH_CALC_NEGATIVE_DENOMINATOR = 0x800007D6L; + public const long PDH_CALC_NEGATIVE_TIMEBASE = 0x800007D7L; + public const long PDH_CALC_NEGATIVE_VALUE = 0x800007D8L; + public const long PDH_DIALOG_CANCELLED = 0x800007D9L; + public const long PDH_END_OF_LOG_FILE = 0x800007DAL; + public const long PDH_ASYNC_QUERY_TIMEOUT = 0x800007DBL; + public const long PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE = 0x800007DCL; + public const long PDH_UNABLE_MAP_NAME_FILES = 0x80000BD5L; + public const long PDH_PLA_VALIDATION_WARNING = 0x80000BF3L; + public const long PDH_CSTATUS_NO_OBJECT = 0xC0000BB8L; + public const long PDH_CSTATUS_NO_COUNTER = 0xC0000BB9L; + public const long PDH_CSTATUS_INVALID_DATA = 0xC0000BBAL; + public const long PDH_MEMORY_ALLOCATION_FAILURE = 0xC0000BBBL; + public const long PDH_INVALID_HANDLE = 0xC0000BBCL; + public const long PDH_INVALID_ARGUMENT = 0xC0000BBDL; + public const long PDH_FUNCTION_NOT_FOUND = 0xC0000BBEL; + public const long PDH_CSTATUS_NO_COUNTERNAME = 0xC0000BBFL; + public const long PDH_CSTATUS_BAD_COUNTERNAME = 0xC0000BC0L; + public const long PDH_INVALID_BUFFER = 0xC0000BC1L; + public const long PDH_INSUFFICIENT_BUFFER = 0xC0000BC2L; + public const long PDH_CANNOT_CONNECT_MACHINE = 0xC0000BC3L; + public const long PDH_INVALID_PATH = 0xC0000BC4L; + public const long PDH_INVALID_INSTANCE = 0xC0000BC5L; + public const long PDH_INVALID_DATA = 0xC0000BC6L; + public const long PDH_NO_DIALOG_DATA = 0xC0000BC7L; + public const long PDH_CANNOT_READ_NAME_STRINGS = 0xC0000BC8L; + public const long PDH_LOG_FILE_CREATE_ERROR = 0xC0000BC9L; + public const long PDH_LOG_FILE_OPEN_ERROR = 0xC0000BCAL; + public const long PDH_LOG_TYPE_NOT_FOUND = 0xC0000BCBL; + public const long PDH_NO_MORE_DATA = 0xC0000BCCL; + public const long PDH_ENTRY_NOT_IN_LOG_FILE = 0xC0000BCDL; + public const long PDH_DATA_SOURCE_IS_LOG_FILE = 0xC0000BCEL; + public const long PDH_DATA_SOURCE_IS_REAL_TIME = 0xC0000BCFL; + public const long PDH_UNABLE_READ_LOG_HEADER = 0xC0000BD0L; + public const long PDH_FILE_NOT_FOUND = 0xC0000BD1L; + public const long PDH_FILE_ALREADY_EXISTS = 0xC0000BD2L; + public const long PDH_NOT_IMPLEMENTED = 0xC0000BD3L; + public const long PDH_STRING_NOT_FOUND = 0xC0000BD4L; + public const long PDH_UNKNOWN_LOG_FORMAT = 0xC0000BD6L; + public const long PDH_UNKNOWN_LOGSVC_COMMAND = 0xC0000BD7L; + public const long PDH_LOGSVC_QUERY_NOT_FOUND = 0xC0000BD8L; + public const long PDH_LOGSVC_NOT_OPENED = 0xC0000BD9L; + public const long PDH_WBEM_ERROR = 0xC0000BDAL; + public const long PDH_ACCESS_DENIED = 0xC0000BDBL; + public const long PDH_LOG_FILE_TOO_SMALL = 0xC0000BDCL; + public const long PDH_INVALID_DATASOURCE = 0xC0000BDDL; + public const long PDH_INVALID_SQLDB = 0xC0000BDEL; + public const long PDH_NO_COUNTERS = 0xC0000BDFL; + public const long PDH_SQL_ALLOC_FAILED = 0xC0000BE0L; + public const long PDH_SQL_ALLOCCON_FAILED = 0xC0000BE1L; + public const long PDH_SQL_EXEC_DIRECT_FAILED = 0xC0000BE2L; + public const long PDH_SQL_FETCH_FAILED = 0xC0000BE3L; + public const long PDH_SQL_ROWCOUNT_FAILED = 0xC0000BE4L; + public const long PDH_SQL_MORE_RESULTS_FAILED = 0xC0000BE5L; + public const long PDH_SQL_CONNECT_FAILED = 0xC0000BE6L; + public const long PDH_SQL_BIND_FAILED = 0xC0000BE7L; + public const long PDH_CANNOT_CONNECT_WMI_SERVER = 0xC0000BE8L; + public const long PDH_PLA_COLLECTION_ALREADY_RUNNING = 0xC0000BE9L; + public const long PDH_PLA_ERROR_SCHEDULE_OVERLAP = 0xC0000BEAL; + public const long PDH_PLA_COLLECTION_NOT_FOUND = 0xC0000BEBL; + public const long PDH_PLA_ERROR_SCHEDULE_ELAPSED = 0xC0000BECL; + public const long PDH_PLA_ERROR_NOSTART = 0xC0000BEDL; + public const long PDH_PLA_ERROR_ALREADY_EXISTS = 0xC0000BEEL; + public const long PDH_PLA_ERROR_TYPE_MISMATCH = 0xC0000BEFL; + public const long PDH_PLA_ERROR_FILEPATH = 0xC0000BF0L; + public const long PDH_PLA_SERVICE_ERROR = 0xC0000BF1L; + public const long PDH_PLA_VALIDATION_ERROR = 0xC0000BF2L; + public const long PDH_PLA_ERROR_NAME_TOO_LONG = 0xC0000BF4L; + public const long PDH_INVALID_SQL_LOG_FORMAT = 0xC0000BF5L; + public const long PDH_COUNTER_ALREADY_IN_QUERY = 0xC0000BF6L; + public const long PDH_BINARY_LOG_CORRUPT = 0xC0000BF7L; + public const long PDH_LOG_SAMPLE_TOO_SMALL = 0xC0000BF8L; + public const long PDH_OS_LATER_VERSION = 0xC0000BF9L; + public const long PDH_OS_EARLIER_VERSION = 0xC0000BFAL; + public const long PDH_INCORRECT_APPEND_TIME = 0xC0000BFBL; + public const long PDH_UNMATCHED_APPEND_COUNTER = 0xC0000BFCL; + public const long PDH_SQL_ALTER_DETAIL_FAILED = 0xC0000BFDL; + public const long PDH_QUERY_PERF_DATA_TIMEOUT = 0xC0000BFEL; + } + + + + internal static class PerfDetail + { + public const uint PERF_DETAIL_NOVICE = 100; // The uninformed can understand it + public const uint PERF_DETAIL_ADVANCED = 200; // For the advanced user + public const uint PERF_DETAIL_EXPERT = 300; // For the expert user + public const uint PERF_DETAIL_WIZARD = 400; // For the system designer + } + + [StructLayout(LayoutKind.Sequential, Pack = 2, Size = 16)] + internal struct SYSTEMTIME + { + public UInt16 year; + public UInt16 month; + public UInt16 dayOfWeek; + public UInt16 day; + public UInt16 hour; + public UInt16 minute; + public UInt16 second; + public UInt16 milliseconds; + } + + internal static class PdhFormat + { + public const uint PDH_FMT_RAW = 0x00000010; + public const uint PDH_FMT_ANSI = 0x00000020; + public const uint PDH_FMT_UNICODE = 0x00000040; + public const uint PDH_FMT_LONG = 0x00000100; + public const uint PDH_FMT_DOUBLE = 0x00000200; + public const uint PDH_FMT_LARGE = 0x00000400; + public const uint PDH_FMT_NOSCALE = 0x00001000; + public const uint PDH_FMT_1000 = 0x00002000; + public const uint PDH_FMT_NODATA = 0x00004000; + public const uint PDH_FMT_NOCAP100 = 0x00008000; + public const uint PERF_DETAIL_COSTLY = 0x00010000; + public const uint PERF_DETAIL_STANDARD = 0x0000FFFF; + } + + internal static class PdhLogAccess + { + public const uint PDH_LOG_READ_ACCESS = 0x00010000; + public const uint PDH_LOG_WRITE_ACCESS = 0x00020000; + public const uint PDH_LOG_UPDATE_ACCESS = 0x00040000; + public const uint PDH_LOG_ACCESS_MASK = 0x000F0000; + } + + internal static class PdhLogOpenMode + { + public const uint PDH_LOG_CREATE_NEW = 0x00000001; + public const uint PDH_LOG_CREATE_ALWAYS = 0x00000002; + public const uint PDH_LOG_OPEN_ALWAYS = 0x00000003; + public const uint PDH_LOG_OPEN_EXISTING = 0x00000004; + public const uint PDH_LOG_CREATE_MASK = 0x0000000F; + } + + internal static class PdhLogOpenOption + { + public const uint PDH_LOG_OPT_USER_STRING = 0x01000000; + public const uint PDH_LOG_OPT_CIRCULAR = 0x02000000; + public const uint PDH_LOG_OPT_MAX_IS_BYTES = 0x04000000; + public const uint PDH_LOG_OPT_APPEND = 0x08000000; + } + + internal enum PdhLogFileType + { + PDH_LOG_TYPE_UNDEFINED = 0, + PDH_LOG_TYPE_CSV = 1, + PDH_LOG_TYPE_TSV = 2, + PDH_LOG_TYPE_TRACE_KERNEL = 4, + PDH_LOG_TYPE_TRACE_GENERIC = 5, + PDH_LOG_TYPE_PERFMON = 6, + PDH_LOG_TYPE_SQL = 7, + PDH_LOG_TYPE_BINARY = 8 + } + + internal static class PdhWildCardFlag + { + public const uint PDH_NOEXPANDCOUNTERS = 1; + public const uint PDH_NOEXPANDINSTANCES = 2; + public const uint PDH_REFRESHCOUNTERS = 4; + } + + internal struct CounterHandleNInstance + { + public IntPtr hCounter; + public string InstanceName; + } + + internal class PdhHelper : IDisposable + { + + private bool m_isPreVista = false; + + public PdhHelper (bool isPreVista) + { + m_isPreVista = isPreVista; + } + + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct PDH_COUNTER_PATH_ELEMENTS + { + [MarshalAs(UnmanagedType.LPWStr)] + public string MachineName; + + [MarshalAs(UnmanagedType.LPWStr)] + public string ObjectName; + + [MarshalAs(UnmanagedType.LPWStr)] + public string InstanceName; + + [MarshalAs(UnmanagedType.LPWStr)] + public string ParentInstance; + + public UInt32 InstanceIndex; + + [MarshalAs(UnmanagedType.LPWStr)] + public string CounterName; + } + + [StructLayout(LayoutKind.Sequential)] + struct PDH_FMT_COUNTERVALUE_LARGE + { + public uint CStatus; + + public Int64 largeValue; + + //[FieldOffset (4), MarshalAs(UnmanagedType.LPStr)] + //public string AnsiStringValue; + + //[FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)] + //public string WideStringValue; + } ; + + [StructLayout(LayoutKind.Sequential)] + struct PDH_FMT_COUNTERVALUE_DOUBLE + { + public uint CStatus; + + public double doubleValue; + } ; + + [StructLayout(LayoutKind.Sequential)] + struct PDH_FMT_COUNTERVALUE_UNICODE + { + public uint CStatus; + + [MarshalAs(UnmanagedType.LPWStr)] + public string WideStringValue; + } ; + + [StructLayout(LayoutKind.Sequential)] + struct PDH_RAW_COUNTER + { + public uint CStatus; + public System.Runtime.InteropServices.ComTypes.FILETIME TimeStamp; + public Int64 FirstValue; + public Int64 SecondValue; + public uint MultiCount; + } + + [StructLayout(LayoutKind.Sequential)] + struct PDH_TIME_INFO + { + public Int64 StartTime; + public Int64 EndTime; + public UInt32 SampleCount; + } + + + /* + // + // This is the structure returned by PdhGetCounterInfo(). + // We only need dwType and lDefaultScale fields from this structure. + // We access those fields directly. The struct is here for reference only. + // + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] + struct PDH_COUNTER_INFO { + [FieldOffset(0)] public UInt32 dwLength; + [FieldOffset(4)] public UInt32 dwType; + [FieldOffset(8)] public UInt32 CVersion; + [FieldOffset(12)] public UInt32 CStatus; + [FieldOffset(16)] public UInt32 lScale; + [FieldOffset(20)] public UInt32 lDefaultScale; + [FieldOffset(24)] public IntPtr dwUserData; + [FieldOffset(32)] public IntPtr dwQueryUserData; + [FieldOffset(40)] public string szFullPath; + + [FieldOffset(48)] public string szMachineName; + [FieldOffset(56)] public string szObjectName; + [FieldOffset(64)] public string szInstanceName; + [FieldOffset(72)] public string szParentInstance; + [FieldOffset(80)] public UInt32 dwInstanceIndex; + [FieldOffset(88)] public string szCounterName; + + [FieldOffset(96)] public string szExplainText; + [FieldOffset(104)]public IntPtr DataBuffer; + }*/ + + + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhBindInputDataSource(out PdhSafeDataSourceHandle phDataSource, string szLogFileNameList); + + [DllImport("pdh.dll")] + private static extern uint PdhOpenQueryH(PdhSafeDataSourceHandle hDataSource, IntPtr dwUserData, out PdhSafeQueryHandle phQuery); + + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhAddCounter(PdhSafeQueryHandle queryHandle, string counterPath, IntPtr userData, out IntPtr counterHandle); + + //Win7+ only + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhAddRelogCounter (PdhSafeQueryHandle queryHandle, string counterPath, + UInt32 counterType, UInt32 counterDefaultScale, + UInt64 timeBase, out IntPtr counterHandle); + + //not on XP + [DllImport("pdh.dll")] + private static extern uint PdhCollectQueryDataWithTime(PdhSafeQueryHandle queryHandle, ref Int64 pllTimeStamp); + + [DllImport("pdh.dll")] + private static extern uint PdhCollectQueryData(PdhSafeQueryHandle queryHandle); + + [DllImport("pdh.dll")] + internal static extern uint PdhCloseQuery(IntPtr queryHandle); + + [DllImport("pdh.dll")] + internal static extern uint PdhCloseLog(IntPtr logHandle, uint dwFlags); + + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhOpenLog(string szLogFileName, + uint dwAccessFlags, + ref PdhLogFileType lpdwLogType, + PdhSafeQueryHandle hQuery, + uint dwMaxSize, + string szUserCaption, + out PdhSafeLogHandle phLog + ); + + //Win7+ only + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern void PdhResetRelogCounterValues(PdhSafeLogHandle LogHandle); + + + //Win7+ only + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhSetCounterValue (IntPtr CounterHandle, + ref PDH_RAW_COUNTER Value, /*PPDH_RAW_COUNTER */ + string InstanceName + ); + + //Win7+ only + [DllImport("pdh.dll")] + private static extern uint PdhWriteRelogSample(PdhSafeLogHandle LogHandle, + Int64 Timestamp + ); + + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhGetFormattedCounterValue(IntPtr counterHandle, uint dwFormat, out IntPtr lpdwType, out PDH_FMT_COUNTERVALUE_DOUBLE pValue); + + [DllImport("pdh.dll", CharSet = CharSet.Unicode)] + private static extern uint PdhGetRawCounterValue(IntPtr hCounter, out IntPtr lpdwType, out PDH_RAW_COUNTER pValue); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhEnumMachinesH(PdhSafeDataSourceHandle hDataSource, IntPtr mszMachineNameList, ref IntPtr pcchBufferLength); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhEnumObjectsH(PdhSafeDataSourceHandle hDataSource, string szMachineName, IntPtr mszObjectList, ref IntPtr pcchBufferLength, uint dwDetailLevel, bool bRefresh); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhEnumObjectItemsH(PdhSafeDataSourceHandle hDataSource, + string szMachineName, + string szObjectName, + IntPtr mszCounterList, + ref IntPtr pcchCounterListLength, + IntPtr mszInstanceList, + ref IntPtr pcchInstanceListLength, + uint dwDetailLevel, + uint dwFlags); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhMakeCounterPath(ref PDH_COUNTER_PATH_ELEMENTS pCounterPathElements, + IntPtr szFullPathBuffer, + ref IntPtr pcchBufferSize, + UInt32 dwFlags); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhParseCounterPath(string szFullPathBuffer, + IntPtr pCounterPathElements, //PDH_COUNTER_PATH_ELEMENTS + ref IntPtr pdwBufferSize, + uint dwFlags ); + + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhExpandWildCardPathH(PdhSafeDataSourceHandle hDataSource, + string szWildCardPath, + IntPtr mszExpandedPathList, + ref IntPtr pcchPathListLength, + uint dwFlags ); + + //not available on XP + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhValidatePathEx(PdhSafeDataSourceHandle hDataSource, string szFullPathBuffer); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhValidatePath(string szFullPathBuffer); + + //not available on XP + [DllImport("pdh.dll", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall, PreserveSig=true)] //private export + private static extern IntPtr PdhGetExplainText( string szMachineName, string szObjectName, string szCounterName); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhGetCounterInfo(IntPtr hCounter, [MarshalAs(UnmanagedType.U1)]bool bRetrieveExplainText, ref IntPtr pdwBufferSize, IntPtr lpBuffer); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhGetCounterTimeBase(IntPtr hCounter, out UInt64 pTimeBase); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhGetDataSourceTimeRangeH(PdhSafeDataSourceHandle hDataSource, ref IntPtr pdwNumEntries, ref PDH_TIME_INFO pInfo, ref IntPtr pdwBufferSize); + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhSetQueryTimeRange(PdhSafeQueryHandle hQuery, ref PDH_TIME_INFO pInfo); + + + [DllImport("pdh.dll", CharSet=CharSet.Unicode)] + private static extern uint PdhLookupPerfNameByIndex(string szMachineName, UInt32 dwNameIndex, IntPtr szNameBuffer, ref int pcchNameBufferSize); + + + private PdhSafeDataSourceHandle m_hDataSource = null; + + private PdhSafeQueryHandle m_hQuery = null; + + private bool m_firstReading = true; + + private PdhSafeLogHandle m_hOutputLog = null; + + // + // Implement IDisposable::Dispose() to close native safe handles + // + public void Dispose() + { + if (m_hDataSource != null && !m_hDataSource.IsInvalid) + { + m_hDataSource.Dispose(); + } + + if (m_hOutputLog != null && !m_hOutputLog.IsInvalid) + { + m_hOutputLog.Dispose(); + } + + if (m_hQuery != null && !m_hQuery.IsInvalid) + { + m_hQuery.Dispose(); + } + + GC.SuppressFinalize(this); + } + + // + // m_ConsumerPathToHandleAndInstanceMap map is used for reading counter date (live or from files). + // + private Dictionary m_ConsumerPathToHandleAndInstanceMap = new Dictionary(); + + + // + // m_ReloggerPathToHandleAndInstanceMap map is used for writing relog counters. + // + private Dictionary m_ReloggerPathToHandleAndInstanceMap = new Dictionary(); + + /// + /// A helper reading in a Unicode string with embedded NULLs and splitting it into a StringCollection. + /// + /// + /// + + private void ReadPdhMultiString(ref IntPtr strNative, Int32 strSize, ref StringCollection strColl) + { + Debug.Assert(strSize >= 2); + int offset = 0; + string allSubstringsWithNulls = ""; + while (offset <= ((strSize * sizeof (char)) - 4)) + { + Int32 next4 = Marshal.ReadInt32(strNative, offset); + if (next4 == 0) + { + break; + } + + allSubstringsWithNulls += (char)next4; + + offset += 2; + } + + allSubstringsWithNulls = allSubstringsWithNulls.TrimEnd('\0'); + + strColl.AddRange(allSubstringsWithNulls.Split('\0')); + } + + + + private uint GetCounterInfoPlus(IntPtr hCounter, out UInt32 counterType, out UInt32 defaultScale, out UInt64 timeBase) + { + uint res = 0; + counterType = 0; + defaultScale = 0; + timeBase = 0; + + Debug.Assert(hCounter != null); + + IntPtr pBufferSize = new IntPtr(0); + res = PdhGetCounterInfo (hCounter, false, ref pBufferSize, IntPtr.Zero); + if (res != PdhResults.PDH_MORE_DATA) + { + return res; + } + + Int32 bufSize = pBufferSize.ToInt32(); + IntPtr bufCounterInfo = Marshal.AllocHGlobal(bufSize); + + try + { + res = PdhGetCounterInfo (hCounter, false, ref pBufferSize, bufCounterInfo); + if (res == 0 && bufCounterInfo != IntPtr.Zero) + { + //PDH_COUNTER_INFO pdhCounterInfo = (PDH_COUNTER_INFO)Marshal.PtrToStructure(bufCounterInfo, typeof(PDH_COUNTER_INFO)); + + counterType = (uint)Marshal.ReadInt32(bufCounterInfo, 4); + defaultScale = (uint)Marshal.ReadInt32(bufCounterInfo, 20); + } + } + finally + { + Marshal.FreeHGlobal(bufCounterInfo); + } + + res = PdhGetCounterTimeBase (hCounter, out timeBase); + if (res != 0) + { + return res; + } + + return res; + } + + public uint ConnectToDataSource() + { + if (m_hDataSource != null && !m_hDataSource.IsInvalid) + { + m_hDataSource.Dispose(); + } + + uint res = PdhHelper.PdhBindInputDataSource(out m_hDataSource, null); + if (res != 0) + { + //Console.WriteLine("error in PdhBindInputDataSource: " + res); + return res; + } + + return 0; + } + /// + /// Connects to a single named datasource, initializing m_hDataSource variable. + /// + /// + /// + public uint ConnectToDataSource(string dataSourceName) + { + if (m_hDataSource != null && !m_hDataSource.IsInvalid) + { + m_hDataSource.Dispose(); + } + + uint res = PdhHelper.PdhBindInputDataSource(out m_hDataSource, dataSourceName); + if (res != 0) + { + //Console.WriteLine("error in PdhBindInputDataSource: " + res); + } + return res; + } + + + public uint ConnectToDataSource(StringCollection blgFileNames) + { + if (blgFileNames.Count == 1) + { + return ConnectToDataSource(blgFileNames[0]); + } + + string doubleNullTerminated = ""; + foreach (string fileName in blgFileNames) + { + doubleNullTerminated += fileName + '\0'; + } + + doubleNullTerminated += '\0'; + + return ConnectToDataSource(doubleNullTerminated); + } + + public uint OpenQuery() + { + uint res = PdhOpenQueryH(m_hDataSource, IntPtr.Zero, out m_hQuery); + + if (res != 0) + { + //Console.WriteLine("error in PdhOpenQueryH: " + res); + } + return res; + } + + public uint OpenLogForWriting(string logName, PdhLogFileType logFileType, bool bOverwrite, UInt32 maxSize, bool bCircular, string caption) + { + Debug.Assert (m_hQuery != null); + + UInt32 accessFlags = PdhLogAccess.PDH_LOG_WRITE_ACCESS; + accessFlags |= bCircular? PdhLogOpenOption.PDH_LOG_OPT_CIRCULAR : 0; + accessFlags |= bOverwrite? PdhLogOpenMode.PDH_LOG_CREATE_ALWAYS : PdhLogOpenMode.PDH_LOG_CREATE_NEW; + + uint res = PdhOpenLog(logName, + accessFlags, + ref logFileType, + m_hQuery, + maxSize, + caption, + out m_hOutputLog); + + return res; + + } + + + public uint SetQueryTimeRange(DateTime startTime, DateTime endTime) + { + Debug.Assert (m_hQuery != null); + Debug.Assert (endTime >= startTime); + + PDH_TIME_INFO pTimeInfo = new PDH_TIME_INFO(); + + if (startTime != DateTime.MinValue && startTime.Kind == DateTimeKind.Local) + { + startTime = new DateTime(startTime.Ticks, DateTimeKind.Utc); + } + pTimeInfo.StartTime = (startTime == DateTime.MinValue) ? 0 : startTime.ToFileTimeUtc(); + + if (endTime != DateTime.MaxValue && endTime.Kind == DateTimeKind.Local) + { + endTime = new DateTime(endTime.Ticks, DateTimeKind.Utc); + } + pTimeInfo.EndTime = (endTime == DateTime.MaxValue) ? Int64.MaxValue : endTime.ToFileTimeUtc(); + + pTimeInfo.SampleCount = 0; + + return PdhSetQueryTimeRange (m_hQuery, ref pTimeInfo); + } + + public uint EnumBlgFilesMachines(ref StringCollection machineNames) + { + + IntPtr MachineListTcharSizePtr = new IntPtr(0); + uint res = PdhHelper.PdhEnumMachinesH(m_hDataSource, IntPtr.Zero, ref MachineListTcharSizePtr); + if (res != PdhResults.PDH_MORE_DATA) + { + return res; + } + + Int32 cChars = MachineListTcharSizePtr.ToInt32(); //should be ok on 64 bit + IntPtr strMachineList = Marshal.AllocHGlobal(cChars * sizeof(char)); + + try + { + res = PdhHelper.PdhEnumMachinesH(m_hDataSource, (IntPtr)strMachineList, ref MachineListTcharSizePtr); + if (res == 0) + { + ReadPdhMultiString(ref strMachineList, MachineListTcharSizePtr.ToInt32(), ref machineNames); + } + } + finally + { + Marshal.FreeHGlobal(strMachineList); + } + + return res; + } + + public uint EnumObjects(string machineName, ref StringCollection objectNames) + { + + IntPtr pBufferSize = new IntPtr(0); + uint res = PdhEnumObjectsH(m_hDataSource, machineName, IntPtr.Zero, ref pBufferSize, PerfDetail.PERF_DETAIL_WIZARD, false); + if (res != PdhResults.PDH_MORE_DATA) + { + return res; + } + + Int32 cChars = pBufferSize.ToInt32(); + IntPtr strObjectList = Marshal.AllocHGlobal(cChars * sizeof(char)); + + try + { + res = PdhEnumObjectsH(m_hDataSource, machineName, (IntPtr)strObjectList, ref pBufferSize, PerfDetail.PERF_DETAIL_WIZARD, false); + if (res == 0) + { + ReadPdhMultiString(ref strObjectList, pBufferSize.ToInt32(), ref objectNames); + } + } + finally + { + Marshal.FreeHGlobal(strObjectList); + } + + return res; + } + + public uint EnumObjectItems(string machineName, string objectName, ref StringCollection counterNames, ref StringCollection instanceNames) + { + + IntPtr pCounterBufferSize = new IntPtr(0); + IntPtr pInstanceBufferSize = new IntPtr(0); + + uint res = PdhEnumObjectItemsH(m_hDataSource, machineName, objectName, + IntPtr.Zero, ref pCounterBufferSize, + IntPtr.Zero, ref pInstanceBufferSize, + PerfDetail.PERF_DETAIL_WIZARD, 0); + if (res == PdhResults.PDH_CSTATUS_NO_INSTANCE) + { + instanceNames.Clear(); + return 0; //masking the error + } + else if (res == PdhResults.PDH_CSTATUS_NO_OBJECT) + { + counterNames.Clear(); + return 0; //masking the error + } + else if (res != PdhResults.PDH_MORE_DATA) + { + //Console.WriteLine("error in PdhEnumObjectItemsH 1st call: " + res); + return res; + } + + Int32 cChars = pCounterBufferSize.ToInt32(); + IntPtr strCountersList = (cChars > 0) ? + Marshal.AllocHGlobal((cChars) * sizeof(char)) : IntPtr.Zero; + //re-set count to 0 if it is lte 2 + if (cChars < 0) + { + pCounterBufferSize = new IntPtr(0); + } + + cChars = pInstanceBufferSize.ToInt32(); + IntPtr strInstancesList = (cChars > 0) ? + Marshal.AllocHGlobal((cChars) * sizeof(char)): IntPtr.Zero; + + //re-set count to 0 if it is lte 2 + if (cChars < 0) + { + pInstanceBufferSize = new IntPtr(0); + } + + try + { + res = PdhEnumObjectItemsH(m_hDataSource, machineName, objectName, + strCountersList, ref pCounterBufferSize, + strInstancesList, ref pInstanceBufferSize, + PerfDetail.PERF_DETAIL_WIZARD, 0); + if (res != 0) + { + //Console.WriteLine("error in PdhEnumObjectItemsH 2nd call: " + res + "\n Counter buffer size is " + // + pCounterBufferSize.ToInt32() + "\n Instance buffer size is " + pInstanceBufferSize.ToInt32()); + } + else + { + ReadPdhMultiString(ref strCountersList, pCounterBufferSize.ToInt32(), ref counterNames); + if (strInstancesList != IntPtr.Zero) + { + ReadPdhMultiString(ref strInstancesList, pInstanceBufferSize.ToInt32(), ref instanceNames); + } + } + } + finally + { + if (strCountersList != IntPtr.Zero) + { + Marshal.FreeHGlobal(strCountersList); + } + if (strInstancesList != IntPtr.Zero) + { + Marshal.FreeHGlobal(strInstancesList); + } + } + + return res; + } + + public uint GetValidPathsFromFiles(ref StringCollection validPaths) + { + + Debug.Assert(m_hDataSource != null && !m_hDataSource.IsInvalid, "Call ConnectToDataSource before GetValidPathsFromFiles"); + + + StringCollection machineNames = new StringCollection(); + uint res = this.EnumBlgFilesMachines(ref machineNames); + if (res != 0) + { + return res; + } + + foreach (string machine in machineNames) + { + StringCollection counterSets = new StringCollection(); + res = this.EnumObjects(machine, ref counterSets); + if (res != 0) + { + return res; + } + + foreach (string counterSet in counterSets) + { + //Console.WriteLine("Counter set " + counterSet); + + StringCollection counterSetCounters = new StringCollection(); + StringCollection counterSetInstances = new StringCollection(); + + res = this.EnumObjectItems(machine , counterSet, ref counterSetCounters, ref counterSetInstances); + if (res != 0) + { + return res; + } + + res = this.GetValidPaths(machine , counterSet, ref counterSetCounters, ref counterSetInstances, ref validPaths); + if (res != 0) + { + return res; + } + } + } + return res; + } + + private bool IsPathValid(ref PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath) + { + bool ret = false; + outPath = ""; + IntPtr pPathBufferSize = new IntPtr(0); + + uint res = PdhMakeCounterPath(ref pathElts, IntPtr.Zero, ref pPathBufferSize, 0); + if (res != PdhResults.PDH_MORE_DATA) + { + return false; + } + + Int32 cChars = pPathBufferSize.ToInt32(); + IntPtr strPath = Marshal.AllocHGlobal(cChars * sizeof(char)); + + try + { + res = PdhMakeCounterPath(ref pathElts, strPath, ref pPathBufferSize, 0); + if (res == 0) + { + outPath = Marshal.PtrToStringUni(strPath); + + if (!m_isPreVista) + { + ret = (PdhValidatePathEx(m_hDataSource, outPath) == 0); + } + else + { + ret = (PdhValidatePath(outPath) == 0); + } + } + } + finally + { + Marshal.FreeHGlobal(strPath); + } + + return ret; + + } + + public bool IsPathValid(string path) + { + if (!m_isPreVista) + { + return (PdhValidatePathEx(m_hDataSource, path) == 0); + } + else + { + // + // Note: this assumes the paths already contain machine names + // + return (PdhValidatePath(path) == 0); + } + } + + + private uint MakePath(PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath, bool bWildcardInstances) + { + outPath = ""; + IntPtr pPathBufferSize = new IntPtr(0); + + if (bWildcardInstances) + { + pathElts.InstanceIndex = 0; + pathElts.InstanceName = "*"; + pathElts.ParentInstance = null; + } + + uint res = PdhMakeCounterPath(ref pathElts, IntPtr.Zero, ref pPathBufferSize, 0); + if (res != PdhResults.PDH_MORE_DATA) + { + return res; + } + + Int32 cChars = pPathBufferSize.ToInt32(); + IntPtr strPath = Marshal.AllocHGlobal(cChars * sizeof(char)); + + try + { + res = PdhMakeCounterPath(ref pathElts, strPath, ref pPathBufferSize, 0); + if (res == 0) + { + outPath = Marshal.PtrToStringUni(strPath); + } + } + finally + { + Marshal.FreeHGlobal(strPath); + } + + return res; + } + + private uint MakeAllInstancePath (string origPath, out string unifiedPath) + { + unifiedPath = origPath; + + PDH_COUNTER_PATH_ELEMENTS elts = new PDH_COUNTER_PATH_ELEMENTS(); + + uint res = ParsePath(origPath, ref elts); + if (res != 0) + { + return res; + } + + return MakePath (elts, out unifiedPath, true); + } + + + private uint ParsePath (string fullPath, ref PDH_COUNTER_PATH_ELEMENTS pCounterPathElements) + { + IntPtr bufSize = new IntPtr(0); + + uint res = PdhParseCounterPath(fullPath, + IntPtr.Zero, + ref bufSize, + 0 ); + if (res != PdhResults.PDH_MORE_DATA && res != 0) + { + //Console.WriteLine("error in PdhParseCounterPath: " + res); + return res; + } + + IntPtr structPtr = Marshal.AllocHGlobal(bufSize.ToInt32()); + + try + { + res = PdhParseCounterPath(fullPath, + structPtr, + ref bufSize, + 0 ); + if (res == 0) + { + // + // Marshal.PtrToStructure will allocate managed memory for the object, + // so the unmanaged ptr can be freed safely + // + pCounterPathElements = (PDH_COUNTER_PATH_ELEMENTS)Marshal.PtrToStructure(structPtr, typeof(PDH_COUNTER_PATH_ELEMENTS)); + } + } + finally + { + Marshal.FreeHGlobal (structPtr); + } + + return res; + } + + // + // TranslateLocalCounterPath() helper translates counter paths from English into the current locale language. + // NOTE: we can only translate counter set and counter names. + // Translated instance names come from providers + // This function will leave them unchanged: + // however, it works for common cases like "*" and "_total" + // and many instance names are just numbers, anyway. + // + // Also - this only supports local paths, b/c connecting to remote registry + // requires a different firewall exception. + // This function checks and Asserts if the path is not valid. + // + public uint TranslateLocalCounterPath(string englishPath, out string localizedPath) + { + uint res = 0; + localizedPath = ""; + PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + res = ParsePath(englishPath, ref pathElts); + if (res != 0) + { + return res; + } + + // Check if the path is local and assert if not: + string machineNameMassaged = pathElts.MachineName.ToLower(CultureInfo.InvariantCulture); + machineNameMassaged = machineNameMassaged.TrimStart('\\'); + Debug.Assert (machineNameMassaged == System.Environment.MachineName.ToLower(CultureInfo.InvariantCulture)); + + string lowerEngCtrName = pathElts.CounterName.ToLower(CultureInfo.InvariantCulture); + string lowerEngObjectName = pathElts.ObjectName.ToLower(CultureInfo.InvariantCulture); + + // Get the registry index + RegistryKey rootKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"); + string[] regCounters = (string[])rootKey.GetValue("Counter"); + + + // NOTE: 1-based enumeration because the name strings follow index strings in the array + Int32 counterIndex = -1; + Int32 objIndex = -1; + for (uint enumIndex = 1; enumIndex < regCounters.Length; enumIndex++) + { + string regString = regCounters[enumIndex]; + if (regString.ToLower(CultureInfo.InvariantCulture) == lowerEngCtrName) + { + try + { + counterIndex = Convert.ToInt32(regCounters[enumIndex-1], CultureInfo.InvariantCulture); + } + catch (Exception) + { + return (uint)PdhResults.PDH_INVALID_PATH; + } + } + else if (regString.ToLower(CultureInfo.InvariantCulture) == lowerEngObjectName) + { + + try + { + objIndex = Convert.ToInt32(regCounters[enumIndex-1], CultureInfo.InvariantCulture); + } + catch (Exception) + { + return (uint)PdhResults.PDH_INVALID_PATH; + } + } + if (counterIndex != -1 && objIndex != -1) + { + break; + } + } + + if (counterIndex == -1 || objIndex == -1) + { + return (uint)PdhResults.PDH_INVALID_PATH; + } + + + // Now, call retrieve the localized names of the object and the counter by index: + string objNameLocalized; + res = LookupPerfNameByIndex(pathElts.MachineName, (uint)objIndex, out objNameLocalized); + if (res != 0) + { + return res; + } + pathElts.ObjectName = objNameLocalized; + + + string ctrNameLocalized; + res = LookupPerfNameByIndex(pathElts.MachineName, (uint)counterIndex, out ctrNameLocalized); + if (res != 0) + { + return res; + } + pathElts.CounterName = ctrNameLocalized; + + // Assemble the path back by using the translated object and counter names: + + res = MakePath(pathElts, out localizedPath, false); + + + return res; + } + + + public uint LookupPerfNameByIndex(string machineName, uint index, out string locName) + { + // + // NOTE: to make PdhLookupPerfNameByIndex() work, + // localizedPath needs to be pre-allocated on the first call. + // This is different from most other PDH functions that tolerate NULL buffers and return required size. + // + + int strSize = 256; + IntPtr localizedPathPtr = Marshal.AllocHGlobal(strSize * sizeof(char)); + locName = ""; + uint res = 0; + try + { + res = PdhLookupPerfNameByIndex(machineName, index, localizedPathPtr, ref strSize); + if (res == PdhResults.PDH_MORE_DATA) + { + Marshal.FreeHGlobal (localizedPathPtr); + localizedPathPtr = Marshal.AllocHGlobal(strSize * sizeof(char)); + res = PdhLookupPerfNameByIndex(machineName, index, localizedPathPtr, ref strSize); + } + if (res == 0) + { + locName = Marshal.PtrToStringUni(localizedPathPtr); + } + } + finally + { + Marshal.FreeHGlobal (localizedPathPtr); + } + + return res; + + } + + + + public uint GetValidPaths (string machineName, + string objectName, + ref StringCollection counters, + ref StringCollection instances, + ref StringCollection validPaths) + { + uint res = 0; + + PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + pathElts.MachineName = machineName; + pathElts.ObjectName = objectName; + + foreach (string counterName in counters) + { + pathElts.CounterName = counterName; + + if (instances.Count == 0) + { + string pathCandidate; + if (IsPathValid(ref pathElts, out pathCandidate)) + { + validPaths.Add(pathCandidate); + } + } + else + { + foreach (string instanceName in instances) + { + pathElts.InstanceName = instanceName; + pathElts.InstanceIndex = 0; + + string pathCandidate; + if (IsPathValid(ref pathElts, out pathCandidate)) + { + validPaths.Add(pathCandidate); + } + } + } + } + return res; + } + + + public uint AddCounters (ref StringCollection validPaths, bool bFlushOldCounters) + { + Debug.Assert(m_hQuery != null && !m_hQuery.IsInvalid); + + if (bFlushOldCounters) + { + m_ConsumerPathToHandleAndInstanceMap.Clear(); + } + + bool bAtLeastOneAdded = false; + uint res = 0; + + foreach (string counterPath in validPaths) + { + IntPtr counterHandle; + res = PdhAddCounter(m_hQuery, counterPath, IntPtr.Zero, out counterHandle); + if (res == 0) + { + CounterHandleNInstance chi = new CounterHandleNInstance(); + chi.hCounter = counterHandle; + chi.InstanceName = null; + + PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + res = ParsePath(counterPath, ref pathElts); + if (res == 0 && pathElts.InstanceName != null) + { + chi.InstanceName = pathElts.InstanceName.ToLower(CultureInfo.InvariantCulture); + } + + if (!m_ConsumerPathToHandleAndInstanceMap.ContainsKey(counterPath.ToLower(CultureInfo.InvariantCulture))) + { + m_ConsumerPathToHandleAndInstanceMap.Add(counterPath.ToLower(CultureInfo.InvariantCulture), chi); + } + + bAtLeastOneAdded = true; + } + } + + return bAtLeastOneAdded ? 0 : res; + } + + + // + // AddRelogCounters combines instances and adds counters to m_hQuery. + // The counter handles and full paths + // + public uint AddRelogCounters(PerformanceCounterSampleSet sampleSet) + { + Debug.Assert(m_hQuery != null && !m_hQuery.IsInvalid); + + uint res = 0; + + Dictionary> prefixInstanceMap = new Dictionary>(); + + // + // Go through all the samples one, constructing prefixInstanceMap and adding new counters as needed + // + foreach (PerformanceCounterSample sample in sampleSet.CounterSamples) + { + PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + res = ParsePath(sample.Path, ref pathElts); + if (res != 0) + { + // Skipping for now, but should be a non-terminating error + continue; + } + + string lowerCaseMachine = pathElts.MachineName.ToLower(CultureInfo.InvariantCulture); + string lowerCaseObject = pathElts.ObjectName.ToLower(CultureInfo.InvariantCulture); + string lowerCaseCounter = pathElts.CounterName.ToLower(CultureInfo.InvariantCulture); + + string lcPathMinusInstance = @"\\" + lowerCaseMachine + @"\" + lowerCaseObject + @"\" + lowerCaseCounter; + + List sampleList; + if (prefixInstanceMap.TryGetValue(lcPathMinusInstance, out sampleList)) + { + prefixInstanceMap[lcPathMinusInstance].Add(sample); + } + else + { + List newList = new List(); + newList.Add (sample); + prefixInstanceMap.Add (lcPathMinusInstance, newList); + } + + //Console.WriteLine ("Added path " + sample.Path + " to the 1ist map with prefix " + lcPathMinusInstance); + } + + // + // Add counters to the query, consolidating multi-instance with a wildcard path, + // and construct m_ReloggerPathToHandleAndInstanceMap where each full path would be pointing to its counter handle + // and an instance name (might be empty for no-instance counter types). + // You can have multiple full paths inside m_ReloggerPathToHandleAndInstanceMap pointing to the same handle. + // + + foreach (string prefix in prefixInstanceMap.Keys) + { + IntPtr counterHandle; + string unifiedPath = prefixInstanceMap[prefix][0].Path; + + if (prefixInstanceMap[prefix].Count > 1) + { + res = MakeAllInstancePath(prefixInstanceMap[prefix][0].Path, out unifiedPath); + if (res != 0) + { + // Skipping for now, but should be a non-terminating error + continue; + } + } + + res = PdhAddRelogCounter(m_hQuery, + unifiedPath, + (UInt32)prefixInstanceMap[prefix][0].CounterType, + prefixInstanceMap[prefix][0].DefaultScale, + prefixInstanceMap[prefix][0].TimeBase, + out counterHandle); + if (res != 0) + { + // Skipping for now, but should be a non-terminating error + // Console.WriteLine ("PdhAddCounter returned " + res + " for counter path " + unifiedPath); + continue; + } + + //Console.WriteLine ("added pdh query path:" + unifiedPath ); + + //now, add all actual paths to m_ReloggerPathToHandleAndInstanceMap + foreach (PerformanceCounterSample sample in prefixInstanceMap[prefix]) + { + PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + res = ParsePath(sample.Path, ref pathElts); + if (res != 0) + { + // Skipping for now, but should be a non-terminating error + continue; + } + + CounterHandleNInstance chi = new CounterHandleNInstance(); + + chi.hCounter = counterHandle; + + if (pathElts.InstanceName != null) + { + chi.InstanceName = pathElts.InstanceName.ToLower(CultureInfo.InvariantCulture); + } + + if (!m_ReloggerPathToHandleAndInstanceMap.ContainsKey(sample.Path.ToLower(CultureInfo.InvariantCulture))) + { + m_ReloggerPathToHandleAndInstanceMap.Add(sample.Path.ToLower(CultureInfo.InvariantCulture), chi); + //Console.WriteLine ("added map path:" + sample.Path ); + } + } + } + + //TODO: verify that all counters are in the map + + return (m_ReloggerPathToHandleAndInstanceMap.Keys.Count > 0) ? 0 : res; + } + + + + // + // AddRelogCountersPreservingPaths preserves all paths and adds as relog counters to m_hQuery. + // The counter handles and full paths are added to m_ReloggerPathToHandleAndInstanceMap + // + public uint AddRelogCountersPreservingPaths(PerformanceCounterSampleSet sampleSet) + { + Debug.Assert(m_hQuery != null && !m_hQuery.IsInvalid); + + uint res = 0; + + // + // Go through all the samples one, constructing prefixInstanceMap and adding new counters as needed + // + foreach (PerformanceCounterSample sample in sampleSet.CounterSamples) + { + PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + res = ParsePath(sample.Path, ref pathElts); + if (res != 0) + { + // Skipping for now, but should be a non-terminating error + continue; + } + + IntPtr counterHandle; + res = PdhAddRelogCounter(m_hQuery, + sample.Path, + (uint)sample.CounterType, + sample.DefaultScale, + sample.TimeBase, + out counterHandle); + if (res != 0) + { + // Skipping for now, but should be a non-terminating error + continue; + } + + CounterHandleNInstance chi = new CounterHandleNInstance(); + + chi.hCounter = counterHandle; + if (pathElts.InstanceName != null) + { + chi.InstanceName = pathElts.InstanceName.ToLower(CultureInfo.InvariantCulture); + } + + if (!m_ReloggerPathToHandleAndInstanceMap.ContainsKey(sample.Path.ToLower(CultureInfo.InvariantCulture))) + { + m_ReloggerPathToHandleAndInstanceMap.Add(sample.Path.ToLower(CultureInfo.InvariantCulture), chi); + } + + } + + return (m_ReloggerPathToHandleAndInstanceMap.Keys.Count > 0) ? 0 : res; + } + + public string GetCounterSetHelp(string szMachineName, string szObjectName) + { + if (m_isPreVista) + { + return string.Empty; + } + IntPtr retString = PdhGetExplainText(szMachineName, szObjectName, null); + return Marshal.PtrToStringUni(retString); + } + + public uint ReadNextSetPreVista (out PerformanceCounterSampleSet nextSet, bool bSkipReading) + { + + uint res = 0; + nextSet = null; + + res = PdhCollectQueryData(m_hQuery); + if (bSkipReading) + { + return res; + } + if (res != 0 && res != PdhResults.PDH_NO_DATA) + { + return res; + } + + PerformanceCounterSample[] samplesArr = new PerformanceCounterSample[m_ConsumerPathToHandleAndInstanceMap.Count]; + uint sampleIndex = 0; + uint numInvalidDataSamples = 0; + uint lastErr = 0; + + DateTime sampleTimeStamp = DateTime.Now; + + foreach (string path in m_ConsumerPathToHandleAndInstanceMap.Keys) + { + IntPtr counterTypePtr = new IntPtr(0); + UInt32 counterType = (UInt32)PerformanceCounterType.RawBase; + UInt32 defaultScale = 0; + UInt64 timeBase = 0; + + IntPtr hCounter = m_ConsumerPathToHandleAndInstanceMap[path].hCounter; + Debug.Assert (hCounter != null); + + res = GetCounterInfoPlus(hCounter, out counterType, out defaultScale, out timeBase); + if (res != 0) + { + //Console.WriteLine ("GetCounterInfoPlus for " + path + " failed with " + res); + } + + PDH_RAW_COUNTER rawValue; + res = PdhGetRawCounterValue(hCounter, out counterTypePtr, out rawValue); + if ( res == PdhResults.PDH_INVALID_DATA || res == PdhResults.PDH_NO_DATA ) + { + //Console.WriteLine ("PdhGetRawCounterValue returned " + res); + samplesArr[sampleIndex++] = new PerformanceCounterSample(path, + m_ConsumerPathToHandleAndInstanceMap[path].InstanceName, + 0, + (ulong)0, + (ulong)0, + 0, + PerformanceCounterType.RawBase, + defaultScale, + timeBase, + DateTime.Now, + (UInt64)DateTime.Now.ToFileTime(), + rawValue.CStatus); + + numInvalidDataSamples++; + lastErr = res; + continue; + } + else if (res != 0) + { + return res; + } + + long dtFT = (((long)rawValue.TimeStamp.dwHighDateTime) << 32) + + (uint)rawValue.TimeStamp.dwLowDateTime; + + // + // NOTE: PDH returns the filetime as local time, therefore + // we need to call FromFileTimUtc() to avoid .NET applying the timezone adjustment. + // However, that would result in the DateTime object having Kind.Utc. + // We have to copy it once more to correct that (Kind is a read-only property). + // + sampleTimeStamp = new DateTime(DateTime.FromFileTimeUtc(dtFT).Ticks, DateTimeKind.Local); + + PDH_FMT_COUNTERVALUE_DOUBLE fmtValueDouble; + res = PdhGetFormattedCounterValue(hCounter, + PdhFormat.PDH_FMT_DOUBLE | PdhFormat.PDH_FMT_NOCAP100, + out counterTypePtr, + out fmtValueDouble); + if (res == PdhResults.PDH_INVALID_DATA || res == PdhResults.PDH_NO_DATA ) + { + //Console.WriteLine ("PdhGetFormattedCounterValue returned " + res); + samplesArr[sampleIndex++] = new PerformanceCounterSample(path, + m_ConsumerPathToHandleAndInstanceMap[path].InstanceName, + 0, + (ulong)rawValue.FirstValue, + (ulong)rawValue.SecondValue, + rawValue.MultiCount, + (PerformanceCounterType)counterType, + defaultScale, + timeBase, + sampleTimeStamp, + (UInt64)dtFT, + fmtValueDouble.CStatus); + + numInvalidDataSamples++; + lastErr = res; + continue; + } + else if (res != 0) + { + //Console.WriteLine ("PdhGetFormattedCounterValue returned " + res); + return res; + } + + samplesArr[sampleIndex++] = new PerformanceCounterSample(path, + m_ConsumerPathToHandleAndInstanceMap[path].InstanceName, + fmtValueDouble.doubleValue, + (ulong)rawValue.FirstValue, + (ulong)rawValue.SecondValue, + rawValue.MultiCount, + (PerformanceCounterType)counterTypePtr.ToInt32(), + defaultScale, + timeBase, + sampleTimeStamp, + (UInt64)dtFT, + fmtValueDouble.CStatus); + + } + + // + // Prior to Vista, PdhCollectQueryDataWithTime() was not available, + // so we could not collect a timestamp for the entire sample set. + // We will use the last sample's timstamp instead. + // + nextSet = new PerformanceCounterSampleSet(sampleTimeStamp, samplesArr, m_firstReading); + m_firstReading = false; + + if (numInvalidDataSamples == samplesArr.Length) + { + res = lastErr; + } + else + { + // + // Reset the error - any errors are saved per sample in PerformanceCounterSample.Status for kvetching later + // + res = 0; + } + + return res; + } + + + public uint ReadNextSet (out PerformanceCounterSampleSet nextSet, bool bSkipReading) + { + Debug.Assert(m_hQuery != null && !m_hQuery.IsInvalid); + + if (m_isPreVista) + { + return ReadNextSetPreVista (out nextSet, bSkipReading); + } + + uint res = 0; + nextSet = null; + + Int64 batchTimeStampFT = 0; + + res = PdhCollectQueryDataWithTime(m_hQuery, ref batchTimeStampFT); + if (bSkipReading) + { + return res; + } + if (res != 0 && res != PdhResults.PDH_NO_DATA) + { + return res; + } + + // + // NOTE: PDH returns the filetime as local time, therefore + // we need to call FromFileTimUtc() to avoid .NET applying the timezone adjustment. + // However, that would result in the DateTime object having Kind.Utc. + // We have to copy it once more to correct that (Kind is a read-only property). + // + DateTime batchStamp = DateTime.Now; + if (res != PdhResults.PDH_NO_DATA) + { + batchStamp = new DateTime (DateTime.FromFileTimeUtc(batchTimeStampFT).Ticks, DateTimeKind.Local); + } + + PerformanceCounterSample[] samplesArr = new PerformanceCounterSample[m_ConsumerPathToHandleAndInstanceMap.Count]; + uint sampleIndex = 0; + uint numInvalidDataSamples = 0; + uint lastErr = 0; + + foreach (string path in m_ConsumerPathToHandleAndInstanceMap.Keys) + { + IntPtr counterTypePtr = new IntPtr(0); + UInt32 counterType = (UInt32)PerformanceCounterType.RawBase; + UInt32 defaultScale = 0; + UInt64 timeBase = 0; + + IntPtr hCounter = m_ConsumerPathToHandleAndInstanceMap[path].hCounter; + Debug.Assert (hCounter != null); + + res = GetCounterInfoPlus(hCounter, out counterType, out defaultScale, out timeBase); + if (res != 0) + { + //Console.WriteLine ("GetCounterInfoPlus for " + path + " failed with " + res); + } + + PDH_RAW_COUNTER rawValue; + res = PdhGetRawCounterValue(hCounter, out counterTypePtr, out rawValue); + if (res != 0) + { + samplesArr[sampleIndex++] = new PerformanceCounterSample(path, + m_ConsumerPathToHandleAndInstanceMap[path].InstanceName, + 0, + (ulong)0, + (ulong)0, + 0, + PerformanceCounterType.RawBase, + defaultScale, + timeBase, + batchStamp, + (UInt64)batchStamp.ToFileTime(), + (rawValue.CStatus == 0) ? res : rawValue.CStatus); + + numInvalidDataSamples++; + lastErr = res; + continue; + } + + long dtFT = (((long)rawValue.TimeStamp.dwHighDateTime) << 32) + + (uint)rawValue.TimeStamp.dwLowDateTime; + + DateTime dt = new DateTime(DateTime.FromFileTimeUtc(dtFT).Ticks, DateTimeKind.Local); + + PDH_FMT_COUNTERVALUE_DOUBLE fmtValueDouble; + res = PdhGetFormattedCounterValue(hCounter, + PdhFormat.PDH_FMT_DOUBLE | PdhFormat.PDH_FMT_NOCAP100, + out counterTypePtr, + out fmtValueDouble); + if (res != 0) + { + samplesArr[sampleIndex++] = new PerformanceCounterSample(path, + m_ConsumerPathToHandleAndInstanceMap[path].InstanceName, + 0, + (ulong)rawValue.FirstValue, + (ulong)rawValue.SecondValue, + rawValue.MultiCount, + (PerformanceCounterType)counterType, + defaultScale, + timeBase, + dt, + (UInt64)dtFT, + (fmtValueDouble.CStatus == 0) ? res : rawValue.CStatus); + + numInvalidDataSamples++; + lastErr = res; + continue; + } + + samplesArr[sampleIndex++] = new PerformanceCounterSample(path, + m_ConsumerPathToHandleAndInstanceMap[path].InstanceName, + fmtValueDouble.doubleValue, + (ulong)rawValue.FirstValue, + (ulong)rawValue.SecondValue, + rawValue.MultiCount, + (PerformanceCounterType)counterTypePtr.ToInt32(), + defaultScale, + timeBase, + dt, + (UInt64)dtFT, + fmtValueDouble.CStatus); + + } + + + nextSet = new PerformanceCounterSampleSet(batchStamp, samplesArr, m_firstReading); + m_firstReading = false; + + if (numInvalidDataSamples == samplesArr.Length) + { + res = lastErr; + } + else + { + // + // Reset the error - any errors are saved per sample in PerformanceCounterSample.Status for kvetching later + // + res = 0; + } + + return res; + } + + + public uint GetFilesSummary(out CounterFileInfo summary) + { + IntPtr pNumEntries = new IntPtr(0); + PDH_TIME_INFO pInfo = new PDH_TIME_INFO(); + IntPtr bufSize = new IntPtr(System.Runtime.InteropServices.Marshal.SizeOf(pInfo)); + + uint res = PdhGetDataSourceTimeRangeH(m_hDataSource, + ref pNumEntries, + ref pInfo, + ref bufSize); + if (res != 0) + { + summary = new CounterFileInfo(); + return res; + } + + summary = new CounterFileInfo (new DateTime (DateTime.FromFileTimeUtc(pInfo.StartTime).Ticks, DateTimeKind.Local), + new DateTime (DateTime.FromFileTimeUtc(pInfo.EndTime).Ticks, DateTimeKind.Local), + pInfo.SampleCount); + + return res; + } + + public uint ExpandWildCardPath (string path, out StringCollection expandedPaths) + { + expandedPaths = new StringCollection(); + IntPtr pcchPathListLength = new IntPtr(0); + + uint res = PdhExpandWildCardPathH(m_hDataSource, + path, + IntPtr.Zero, + ref pcchPathListLength, + PdhWildCardFlag.PDH_REFRESHCOUNTERS); + + if (res != PdhResults.PDH_MORE_DATA) + { + return res; + } + + Int32 cChars = pcchPathListLength.ToInt32(); + IntPtr strPathList = Marshal.AllocHGlobal(cChars * sizeof(char)); + + try + { + res = PdhExpandWildCardPathH(m_hDataSource, path, strPathList, ref pcchPathListLength, PdhWildCardFlag.PDH_REFRESHCOUNTERS); + if (res == 0) + { + ReadPdhMultiString(ref strPathList, pcchPathListLength.ToInt32(), ref expandedPaths); + } + } + finally + { + Marshal.FreeHGlobal (strPathList); + } + + return res; + } + + public void ResetRelogValues () + { + Debug.Assert(m_hOutputLog != null && !m_hOutputLog.IsInvalid); + PdhResetRelogCounterValues(m_hOutputLog); + } + + public uint WriteRelogSample (DateTime timeStamp) + { + Debug.Assert(m_hOutputLog != null && !m_hOutputLog.IsInvalid); + return PdhWriteRelogSample(m_hOutputLog, (new DateTime(timeStamp.Ticks, DateTimeKind.Utc)).ToFileTimeUtc() ); + + } + + public uint SetCounterValue (PerformanceCounterSample sample, out bool bUnknownPath) + { + Debug.Assert(m_hOutputLog != null && !m_hOutputLog.IsInvalid); + + bUnknownPath = false; + + string lcPath = sample.Path.ToLower(CultureInfo.InvariantCulture); + + if (!m_ReloggerPathToHandleAndInstanceMap.ContainsKey(lcPath)) + { + bUnknownPath = true; + return 0; + } + + PDH_RAW_COUNTER rawStruct = new PDH_RAW_COUNTER(); + rawStruct.FirstValue = (long)sample.RawValue; + rawStruct.SecondValue = (long)sample.SecondValue; + rawStruct.MultiCount = sample.MultipleCount; + rawStruct.TimeStamp.dwHighDateTime = (int)((new DateTime(sample.Timestamp.Ticks, DateTimeKind.Utc).ToFileTimeUtc() >> 32) & 0xFFFFFFFFL); + rawStruct.TimeStamp.dwLowDateTime = (int)(new DateTime(sample.Timestamp.Ticks, DateTimeKind.Utc).ToFileTimeUtc() & 0xFFFFFFFFL); + rawStruct.CStatus = sample.Status; + + + return PdhSetCounterValue (m_ReloggerPathToHandleAndInstanceMap[lcPath].hCounter, + ref rawStruct, /*PPDH_RAW_COUNTER */ + m_ReloggerPathToHandleAndInstanceMap[lcPath].InstanceName); + } + + + } +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs new file mode 100644 index 000000000..756a24c00 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs @@ -0,0 +1,77 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +using System; +using System.Runtime.InteropServices; +using System.Runtime.ConstrainedExecution; + +namespace Microsoft.Powershell.Commands.GetCounter.PdhNative +{ + + internal sealed class PdhSafeDataSourceHandle : SafeHandle + { + private PdhSafeDataSourceHandle() : base(IntPtr.Zero, true) { } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + return (PdhHelper.PdhCloseLog(handle, 0) == 0); + } + } + + + + + internal sealed class PdhSafeQueryHandle : SafeHandle + { + private PdhSafeQueryHandle() : base(IntPtr.Zero, true) { } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + + return (PdhHelper.PdhCloseQuery(handle) == 0); + } + } + + internal sealed class PdhSafeLogHandle : SafeHandle + { + private PdhSafeLogHandle() : base(IntPtr.Zero, true) { } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + + return (PdhHelper.PdhCloseLog(handle, 0) == 0); + } + } +} + + From 7f7ce0617ceb4570a933112671c6b708997602ab Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Tue, 14 Jun 2016 17:57:14 -0700 Subject: [PATCH 5/8] Fix project.json in diagnostics --- .../project.json | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/project.json b/src/Microsoft.PowerShell.Commands.Diagnostics/project.json index ccd238c41..a8e781301 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/project.json +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/project.json @@ -1,35 +1,44 @@ { - "name": "Microsoft.PowerShell.Commands.Diagnostics", - "version": "1.0.0-*", - "authors": [ "garretts", "andschwa" ], - - "compilationOptions": { - "warningsAsErrors": true, - "allowUnsafe": true - }, - + "name": "Microsoft.PowerShell.Commands.Diagnostics", + "version": "1.0.0-*", + "authors": [ + "cchen", + "sevoroby" + ], + "buildOptions": { + "warningsAsErrors": true + }, "dependencies": { "System.Management.Automation": "1.0.0-*" }, - - "frameworks": { - "netstandard1.5": { - "imports": [ "dnxcore50" ], - "compilationOptions": { - "define": [ "CORECLR" ] - }, - "buildOptions": { - "compile": { - "excludeFiles": [ "GetEventCommand.cs","PdhSafeHandle.cs","GetEventSnapin.cs","CounterSample.cs","CounterSet.cs","PdhHelper.cs" ,"ExportCounterCommand.cs","GetCounterCommand.cs","ImportCounterCommand.cs","NewWinEventCommand.cs"] - } + "frameworks": { + "netstandard1.5": { + "imports": [ + "dnxcore50" + ], + "buildOptions": { + "define": [ + "CORECLR" + ], + "compile": { + "excludeFiles": [ + "PdhSafeHandle.cs", + "GetEventSnapin.cs", + "CounterSample.cs", + "CounterSet.cs", + "PdhHelper.cs", + "ExportCounterCommand.cs", + "GetCounterCommand.cs", + "ImportCounterCommand.cs", + "CounterFileInfo.cs", + "CommonUtils.cs" + ] } - }, - "net451": { - "frameworkAssemblies": { - "System": "" - } } }, - - "compile": [ "../windows-build/gen/Microsoft.PowerShell.Commands.Diagnostics/*.cs" ] -} + "net451": { + "frameworkAssemblies": { + } + } + } +} \ No newline at end of file From d578e5f426cf7e2d1704bb1355271401899440fd Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Tue, 14 Jun 2016 18:14:20 -0700 Subject: [PATCH 6/8] update project.json for consolehost to include microsoft.powershell.diagnostic --- src/Microsoft.PowerShell.ConsoleHost/project.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.PowerShell.ConsoleHost/project.json b/src/Microsoft.PowerShell.ConsoleHost/project.json index e5644d200..e2b59dc7d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/project.json +++ b/src/Microsoft.PowerShell.ConsoleHost/project.json @@ -28,6 +28,7 @@ "dependencies": { "Microsoft.PowerShell.PSReadLine": "1.0.0-*", + "Microsoft.PowerShell.Commands.Diagnostics": "1.0.0-*", "Microsoft.PowerShell.Commands.Management": "1.0.0-*", "Microsoft.PowerShell.Commands.Utility": "1.0.0-*", "Microsoft.PowerShell.LocalAccounts": "1.0.0-*" From 37e78656af33dfa50d356c8de76b5e566078c77a Mon Sep 17 00:00:00 2001 From: Sergei Vorobev Date: Wed, 15 Jun 2016 15:30:19 -0700 Subject: [PATCH 7/8] Fix project.json and map.json in Microsoft.PowerShell.Commands.Diagnostics --- src/Microsoft.PowerShell.Commands.Diagnostics/map.json | 3 ++- src/Microsoft.PowerShell.Commands.Diagnostics/project.json | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/map.json b/src/Microsoft.PowerShell.Commands.Diagnostics/map.json index 17d19fdbe..e3fdd2fde 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/map.json +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/map.json @@ -10,5 +10,6 @@ "wmi/Events/GetEvent/src/ImportCounterCommand.cs": "ImportCounterCommand.cs", "wmi/Events/GetEvent/src/NewWinEventCommand.cs": "NewWinEventCommand.cs", "wmi/Events/GetEvent/src/PdhHelper.cs": "PdhHelper.cs", - "wmi/Events/GetEvent/src/PdhSafeHandle.cs": "PdhSafeHandle.cs" + "wmi/Events/GetEvent/src/PdhSafeHandle.cs": "PdhSafeHandle.cs", + "wmi/Events/GetEvent/src/resources/GetEventResources.txt": "resources/GetEventResources.txt" } diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/project.json b/src/Microsoft.PowerShell.Commands.Diagnostics/project.json index a8e781301..54f82ee5c 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/project.json +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/project.json @@ -37,8 +37,6 @@ } }, "net451": { - "frameworkAssemblies": { - } } } } \ No newline at end of file From 549eb64da91e8b3324c29c0deb37f44fa2815135 Mon Sep 17 00:00:00 2001 From: PowerShell Team Date: Wed, 15 Jun 2016 15:32:21 -0700 Subject: [PATCH 8/8] Copy resources files for Microsoft.PowerShell.Commands.Diagnostics [SD:714215] Commit 2e2daeaff52930d981a31e0030d7161c4b45cc48 --- .../resources/GetEventResources.txt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt b/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt new file mode 100644 index 000000000..023f6de70 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt @@ -0,0 +1,60 @@ +#Copyright (c) 2008 Microsoft Corporation + +Vendor=Microsoft +Description=This Windows PowerShell snap-in contains Windows Eventing and Performance Counter cmdlets. + +NoMatchingEventsFound=No events were found that match the specified selection criteria. +NoMatchingLogsFound=There is not an event log on the {0} computer that matches "{1}". +NoMatchingProvidersFound=There is not an event provider on the {0} computer that matches "{1}". +NotALogFile=The {0} file does not appear to be a valid log file. Specify only .evtx, .etl, or .evt files as values of the Path parameter. +NotAFileSystemPath=The {0} path does not appear to be a valid log file path. Specify a valid file system path. +NoProviderFound=No provider found with name {0}. +NoEventMessage=Cannot retrieve event message text. + +QueryTrace=Constructed structured query:\n{0}. +LogProviderOrPathNeeded=You must specify at least one Log, Provider or Path key-value pair. +KeywordLongExpected=Invalid value '{0}' specified for keyword. +DateTimeExpected=The following value is not in a valid DateTime format: {0}. +InvalidSIDFormat=The following value is not in a valid security identifier (SID) format: {0}. Enter a valid SID, such as S-1-5-32-544. +InvalidContext=The {0} Context key-value is not a valid SID or NT account name. +ProviderLogLink=The {0} provider writes events to the {1} log. +SpecifiedProvidersDontWriteToLog=The specified providers do not write events to the {0} log. This log will be ignored. +LogsAndProvidersDontOverlap=The specified providers do not write events to any of the specified logs. +ProviderMetadataUnavailable=Could not retrieve information about the {0} provider. Error: {1}. +LogInfoUnavailable=Could not retrieve information about the {0} log. Error: {1}. +SpecifyOldestForLog=The {0} event log can be read only in the forward chronological order because it is an analytical or a debug log. To see events from the {0} event log, use the Oldest parameter in the command. +SpecifyOldestForEtlEvt=The {0} event log file can be read only in the forward chronological order because it is an .etl or an .evt file. To see events from the {0} event log, use the Oldest parameter in the command. +NullNotAllowedInHashtable=A null value was encountered in the {0} hash table key. Null values are not permitted. +IncorrectEventId=Event cannot be written because there are no events defined with id {0} for the provider {1}. Please correct the event id and try again. +IncorrectEventVersion=Event cannot be written because the specified version {0} for event {1} is not defined for the provider {2}. Please correct the version and try again. +VersionNotSpecified=Event cannot be written because multiple events with id {0} have beend defined for provider {1}. Please provide a version for the event and try again. +ProviderNotSpecified=Provider name must be specified. +EventIdNotSpecified=A valid Event Id must be specified. +PayloadMismatch=Provided payload does not match with the template that was defined for event id {0}. \r\nThe defined template is following: \r\n{1} + +Timestamp=Timestamp +CookedValues=Cooked Values +CounterApiError=Internal performance counter API call failed. Error: {0:x8}. +CounterPathIsInvalid=The {0} performance counter path is not valid. +CounterPathInvalidOrNotInFile=The {0} performance counter path is either not valid or it is not present in the following files: {1}. +CounterPathsInFilesInvalid=No valid counter paths were found in the files. +CounterSetEnumAccessDenied=Cannot retrieve information about the {0} performance counter set because access was denied. +CounterInvalidDateRange=The value of the StartTime parameter must be less than the value of the EndTime parameter. +Counter32FileLimit=You cannot import more than 32 .blg counter log files in each command. +Counter1FileLimit=You cannot import more than one comma-separated (.csv) or tab-separated (.tsv) performance counter file in each command. +CounterNotALogFile=The {0} file does not have the expected file name extension. Specify only .blg, .csv, or .tsv files when you use the Path parameter. +CounterNoMixedLogTypes=You cannot import different types of performance log files in the same command. Specify only one type of file in the Path parameter. +NoMatchingCounterSetsFound=Cannot find any performance counter sets on the {0} computer that match the following: {1}. +NoMatchingCounterSetsInFile=Cannot find any performance counter sets in the {0} files that match the following: {1}. +NoCounterSetsOnComputer=Could not find any performance counter sets on the {0} computer: error {1:x8}. Verify that the {0} computer exists, that it is discoverable, and that you have sufficient privileges to view performance counter data on that computer. +CounterFileExists=The {0} file already exists. To overwrite this file, use the Force parameter in the Export-Counter command. +CounterExportSampleNotInInitialSet=The {0} performance counter cannot be exported to the {1} file because it was not part of the first sample set. +CounterPathTranslationFailed=The default values for this command failed. Error: {0:x8}. +CounterContinuousOrMaxSamples=The Continuous parameter and the MaxSamples parameter cannot be used in the same command. +CounterSampleDataInvalid=The data in one of the performance counter samples is not valid. View the Status property for each PerformanceCounterSample object to make sure it contains valid data. +CounterInvalidFormat=The {0} format type is not a valid format for a counter file. Possible values for the Format parameter are .blg, .csv, or .tsv. +CounterCircularNoMaxSize=The Circular parameter will be ignored unless the MaxSize parameter is also specified. +ExportCtrWin7Required=This cmdlet can be run only on Microsoft Windows 7 and above. +FileOpenFailed=Unable to open the {0} file for writing. +FileCreateFailed=Unable to create the {0} file. Verify that the path is valid. +ExportDestPathAmbiguous=The following export destination path is ambiguous: {0}. \ No newline at end of file