VB.NET 在 Windows下通過WIn32API獲取CPU和內存的使用率

.net 要獲取CPU和內存的使用率,一般是通過 PerformanceCounter 或者 WMI 查詢得到,但是如果操作系統經常不正常斷電或者別的什麼原因,讓系統的性能計數器抽風了,可能就會造成初始化 PerformanceCounter 對象出錯。

性能計數器錯誤一般可以通過 lodctr /r 可以修復,但是時不時要這麼搞一下,對用戶總是太不友好了,所以能通過 Win32API 來獲取也是一個不錯的選項。

代碼已封裝到兩個工具類 CpuUsageNt 和 MemUsage 裏面了

''' <summary>
''' Inherits the CPUUsage class and implements the Query method for Windows NT systems.
''' </summary>
''' <remarks>
''' <p>This class works on Windows NT4, Windows 2000, Windows XP, Windows .NET Server and higher.</p>
''' </remarks>
Private NotInheritable Class CpuUsageNt
    ''' <summary>
    ''' Initializes a new CpuUsageNt instance.
    ''' </summary>
    ''' <exception cref="NotSupportedException">One of the system calls fails.</exception>
    Public Sub New()
        Dim timeInfo(31) As Byte ' SYSTEM_TIME_INFORMATION structure
        Dim perfInfo(311) As Byte ' SYSTEM_PERFORMANCE_INFORMATION structure
        Dim baseInfo(43) As Byte ' SYSTEM_BASIC_INFORMATION structure
        Dim ret As Integer

        If (Environment.OSVersion.Platform <> PlatformID.Win32NT) Then
            Throw New NotSupportedException()
        End If

        ' get new system time
        ret = NtQuerySystemInformation(SYSTEM_TIMEINFORMATION, timeInfo, timeInfo.Length, IntPtr.Zero)
        If ret <> NO_ERROR Then
            Throw New NotSupportedException()
        End If
        ' get new CPU's idle time
        ret = NtQuerySystemInformation(SYSTEM_PERFORMANCEINFORMATION, perfInfo, perfInfo.Length, IntPtr.Zero)
        If ret <> NO_ERROR Then
            Throw New NotSupportedException()
        End If
        ' get number of processors in the system
        ret = NtQuerySystemInformation(SYSTEM_BASICINFORMATION, baseInfo, baseInfo.Length, IntPtr.Zero)
        If ret <> NO_ERROR Then
            Throw New NotSupportedException()
        End If
        ' store new CPU's idle and system time and number of processors
        _oldIdleTime = BitConverter.ToInt64(perfInfo, 0) ' SYSTEM_PERFORMANCE_INFORMATION.liIdleTime
        _oldSystemTime = BitConverter.ToInt64(timeInfo, 8) ' SYSTEM_TIME_INFORMATION.liKeSystemTime
        _processorCount = baseInfo(40)
    End Sub

    ''' <summary>
    ''' Determines the current average CPU load.
    ''' </summary>
    ''' <returns>An integer that holds the CPU load percentage.</returns>
    ''' <exception cref="NotSupportedException">One of the system calls fails. The CPU time can not be obtained.</exception>
    Public Function Query() As Integer
        Dim timeInfo(31) As Byte ' SYSTEM_TIME_INFORMATION structure
        Dim perfInfo(311) As Byte ' SYSTEM_PERFORMANCE_INFORMATION structure

        ' get new system time
        Dim ret As Integer = NtQuerySystemInformation(SYSTEM_TIMEINFORMATION, timeInfo, timeInfo.Length, IntPtr.Zero)
        If ret <> NO_ERROR Then
            Throw New NotSupportedException()
        End If
        ' get new CPU's idle time
        ret = NtQuerySystemInformation(SYSTEM_PERFORMANCEINFORMATION, perfInfo, perfInfo.Length, IntPtr.Zero)
        If ret <> NO_ERROR Then
            Throw New NotSupportedException()
        End If
        ' CurrentValue = NewValue - OldValue
        Dim dbIdleTime As Double = BitConverter.ToInt64(perfInfo, 0) - _oldIdleTime
        Dim dbSystemTime As Double = BitConverter.ToInt64(timeInfo, 8) - _oldSystemTime
        ' CurrentCpuIdle = IdleTime / SystemTime
        If dbSystemTime <> 0 Then
            dbIdleTime = dbIdleTime / dbSystemTime
        End If
        ' CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
        dbIdleTime = 100.0 - dbIdleTime * 100.0 / _processorCount + 0.5
        ' store new CPU's idle and system time
        _oldIdleTime = BitConverter.ToInt64(perfInfo, 0) ' SYSTEM_PERFORMANCE_INFORMATION.liIdleTime
        _oldSystemTime = BitConverter.ToInt64(timeInfo, 8) ' SYSTEM_TIME_INFORMATION.liKeSystemTime
        Return CInt(Fix(dbIdleTime))
    End Function

    ''' <summary>
    ''' NtQuerySystemInformation is an internal Windows function that retrieves various kinds of system information.
    ''' </summary>
    ''' <param name="dwInfoType">One of the values enumerated in SYSTEM_INFORMATION_CLASS, indicating the kind of system information to be retrieved.</param>
    ''' <param name="lpStructure">Points to a buffer where the requested information is to be returned. The size and structure of this information varies depending on the value of the SystemInformationClass parameter.</param>
    ''' <param name="dwSize">Length of the buffer pointed to by the SystemInformation parameter.</param>
    ''' <param name="returnLength">Optional pointer to a location where the function writes the actual size of the information requested.</param>
    ''' <returns>Returns a success NTSTATUS if successful, and an NTSTATUS error code otherwise.</returns>
    <DllImport("ntdll", EntryPoint:="NtQuerySystemInformation")>
    Private Shared Function NtQuerySystemInformation(ByVal dwInfoType As Integer,
                                                     ByVal lpStructure() As Byte,
                                                     ByVal dwSize As Integer,
                                                     ByVal returnLength As IntPtr) As Integer
    End Function

    ''' <summary>Returns the number of processors in the system in a SYSTEM_BASIC_INFORMATION structure.</summary>
    Private Const SYSTEM_BASICINFORMATION As Integer = 0
    ''' <summary>Returns an opaque SYSTEM_PERFORMANCE_INFORMATION structure.</summary>
    Private Const SYSTEM_PERFORMANCEINFORMATION As Integer = 2
    ''' <summary>Returns an opaque SYSTEM_TIMEOFDAY_INFORMATION structure.</summary>
    Private Const SYSTEM_TIMEINFORMATION As Integer = 3
    ''' <summary>The value returned by NtQuerySystemInformation is no error occurred.</summary>
    Private Const NO_ERROR As Integer = 0
    ''' <summary>Holds the old idle time.</summary>
    Private _oldIdleTime As Long
    ''' <summary>Holds the old system time.</summary>
    Private _oldSystemTime As Long
    ''' <summary>Holds the number of processors in the system.</summary>
    Private _processorCount As Double
End Class
CpuUsageNt
 1 Private NotInheritable Class MemUsage
 2     <StructLayout(LayoutKind.Sequential)>
 3     Private Structure MEMORY_INFO
 4         ''' <summary>
 5         ''' 當前結構體大小
 6         ''' </summary>
 7         Public dwLength As UInteger
 8         ''' <summary>
 9         ''' 當前內存使用率
10         ''' </summary>
11         Public dwMemoryLoad As UInteger
12         ''' <summary>
13         ''' 總計物理內存大小
14         ''' </summary>
15         Public dwTotalPhys As UInteger
16         ''' <summary>
17         ''' 可用物理內存大小
18         ''' </summary>
19         Public dwAvailPhys As UInteger
20         ''' <summary>
21         ''' 總計交換文件大小
22         ''' </summary>
23         Public dwTotalPageFile As UInteger
24         ''' <summary>
25         ''' 可用交換文件大小
26         ''' </summary>
27         Public dwAvailPageFile As UInteger
28         ''' <summary>
29         ''' 總計虛擬內存大小
30         ''' </summary>
31         Public dwTotalVirtual As UInteger
32         ''' <summary>
33         ''' 可用虛擬內存大小
34         ''' </summary>
35         Public dwAvailVirtual As UInteger
36     End Structure
37     <DllImport("kernel32")>
38     Private Shared Sub GlobalMemoryStatus(ByRef meminfo As MEMORY_INFO)
39     End Sub
40 
41     Public Function Query() As Int32
42         Dim iRet As Int32 = 0
43         Try
44             Dim MemInfo As New MEMORY_INFO
45             GlobalMemoryStatus(MemInfo)
46             iRet = CInt(MemInfo.dwMemoryLoad)
47             Debug.Print("[DEBUG] GlobalMemoryStatus dwMemoryLoad={0}", iRet)
48         Catch ex As Exception
49             Debug.Print("[WARN ] GlobalMemoryStatus Err:{0}", ex.Message)
50         End Try
51         Return iRet
52     End Function
53 
54     ''' <summary>
55     ''' 格式化容量大小
56     ''' </summary>
57     ''' <param name="size">容量(B)</param>
58     ''' <returns>已格式化的容量</returns>
59     Private Shared Function FormatSize(ByVal size As Double) As String
60         Dim d As Double = size
61         Dim i As Integer = 0
62         Do While (d > 1024) AndAlso (i < 5)
63             d /= 1024
64             i += 1
65         Loop
66         Dim unit() As String = {"B", "KB", "MB", "GB", "TB"}
67         Return (String.Format("{0} {1}", Math.Round(d, 2), unit(i)))
68     End Function
69 End Class
MemUsage

 

參考資料

1.C#獲得Cpu使用率

2.c# 獲取某一個進程的cpu和內存使用情況

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章