// // 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 _isPreVista = false; public PdhHelper(bool isPreVista) { _isPreVista = isPreVista; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private 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)] private 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)] private struct PDH_FMT_COUNTERVALUE_DOUBLE { public uint CStatus; public double doubleValue; }; [StructLayout(LayoutKind.Sequential)] private struct PDH_FMT_COUNTERVALUE_UNICODE { public uint CStatus; [MarshalAs(UnmanagedType.LPWStr)] public string WideStringValue; }; [StructLayout(LayoutKind.Sequential)] private 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)] private 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 _hDataSource = null; private PdhSafeQueryHandle _hQuery = null; private bool _firstReading = true; private PdhSafeLogHandle _hOutputLog = null; // // Implement IDisposable::Dispose() to close native safe handles // public void Dispose() { if (_hDataSource != null && !_hDataSource.IsInvalid) { _hDataSource.Dispose(); } if (_hOutputLog != null && !_hOutputLog.IsInvalid) { _hOutputLog.Dispose(); } if (_hQuery != null && !_hQuery.IsInvalid) { _hQuery.Dispose(); } GC.SuppressFinalize(this); } // // m_ConsumerPathToHandleAndInstanceMap map is used for reading counter date (live or from files). // private Dictionary _consumerPathToHandleAndInstanceMap = new Dictionary(); // // m_ReloggerPathToHandleAndInstanceMap map is used for writing relog counters. // private Dictionary _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 (_hDataSource != null && !_hDataSource.IsInvalid) { _hDataSource.Dispose(); } uint res = PdhHelper.PdhBindInputDataSource(out _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 (_hDataSource != null && !_hDataSource.IsInvalid) { _hDataSource.Dispose(); } uint res = PdhHelper.PdhBindInputDataSource(out _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(_hDataSource, IntPtr.Zero, out _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(_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, _hQuery, maxSize, caption, out _hOutputLog); return res; } public uint SetQueryTimeRange(DateTime startTime, DateTime endTime) { Debug.Assert(_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(_hQuery, ref pTimeInfo); } public uint EnumBlgFilesMachines(ref StringCollection machineNames) { IntPtr MachineListTcharSizePtr = new IntPtr(0); uint res = PdhHelper.PdhEnumMachinesH(_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(_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(_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(_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(_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(_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(_hDataSource != null && !_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 (!_isPreVista) { ret = (PdhValidatePathEx(_hDataSource, outPath) == 0); } else { ret = (PdhValidatePath(outPath) == 0); } } } finally { Marshal.FreeHGlobal(strPath); } return ret; } public bool IsPathValid(string path) { if (!_isPreVista) { return (PdhValidatePathEx(_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(_hQuery != null && !_hQuery.IsInvalid); if (bFlushOldCounters) { _consumerPathToHandleAndInstanceMap.Clear(); } bool bAtLeastOneAdded = false; uint res = 0; foreach (string counterPath in validPaths) { IntPtr counterHandle; res = PdhAddCounter(_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 (!_consumerPathToHandleAndInstanceMap.ContainsKey(counterPath.ToLower(CultureInfo.InvariantCulture))) { _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(_hQuery != null && !_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(_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 (!_reloggerPathToHandleAndInstanceMap.ContainsKey(sample.Path.ToLower(CultureInfo.InvariantCulture))) { _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 (_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(_hQuery != null && !_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(_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 (!_reloggerPathToHandleAndInstanceMap.ContainsKey(sample.Path.ToLower(CultureInfo.InvariantCulture))) { _reloggerPathToHandleAndInstanceMap.Add(sample.Path.ToLower(CultureInfo.InvariantCulture), chi); } } return (_reloggerPathToHandleAndInstanceMap.Keys.Count > 0) ? 0 : res; } public string GetCounterSetHelp(string szMachineName, string szObjectName) { if (_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(_hQuery); if (bSkipReading) { return res; } if (res != 0 && res != PdhResults.PDH_NO_DATA) { return res; } PerformanceCounterSample[] samplesArr = new PerformanceCounterSample[_consumerPathToHandleAndInstanceMap.Count]; uint sampleIndex = 0; uint numInvalidDataSamples = 0; uint lastErr = 0; DateTime sampleTimeStamp = DateTime.Now; foreach (string path in _consumerPathToHandleAndInstanceMap.Keys) { IntPtr counterTypePtr = new IntPtr(0); UInt32 counterType = (UInt32)PerformanceCounterType.RawBase; UInt32 defaultScale = 0; UInt64 timeBase = 0; IntPtr hCounter = _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, _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, _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, _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, _firstReading); _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(_hQuery != null && !_hQuery.IsInvalid); if (_isPreVista) { return ReadNextSetPreVista(out nextSet, bSkipReading); } uint res = 0; nextSet = null; Int64 batchTimeStampFT = 0; res = PdhCollectQueryDataWithTime(_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[_consumerPathToHandleAndInstanceMap.Count]; uint sampleIndex = 0; uint numInvalidDataSamples = 0; uint lastErr = 0; foreach (string path in _consumerPathToHandleAndInstanceMap.Keys) { IntPtr counterTypePtr = new IntPtr(0); UInt32 counterType = (UInt32)PerformanceCounterType.RawBase; UInt32 defaultScale = 0; UInt64 timeBase = 0; IntPtr hCounter = _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, _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, _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, _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, _firstReading); _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(_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(_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(_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(_hOutputLog != null && !_hOutputLog.IsInvalid); PdhResetRelogCounterValues(_hOutputLog); } public uint WriteRelogSample(DateTime timeStamp) { Debug.Assert(_hOutputLog != null && !_hOutputLog.IsInvalid); return PdhWriteRelogSample(_hOutputLog, (new DateTime(timeStamp.Ticks, DateTimeKind.Utc)).ToFileTimeUtc()); } public uint SetCounterValue(PerformanceCounterSample sample, out bool bUnknownPath) { Debug.Assert(_hOutputLog != null && !_hOutputLog.IsInvalid); bUnknownPath = false; string lcPath = sample.Path.ToLower(CultureInfo.InvariantCulture); if (!_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(_reloggerPathToHandleAndInstanceMap[lcPath].hCounter, ref rawStruct, /*PPDH_RAW_COUNTER */ _reloggerPathToHandleAndInstanceMap[lcPath].InstanceName); } } }