How to obtain information about CPU, RAM, GPU

梓珺 张 40 Reputation points Volunteer Moderator
2025-06-09T09:32:24.4133333+00:00

I have been making a small tool recently

One of the functions requires obtaining the current CPU, RAM, GPU usage rate of the system

How can I currently obtain the usage rate of CPU, RAM, and GPU? Are there any APIs available?

My tool is compatible with Windows versions currently supported by Microsoft

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,772 questions
{count} votes

2 answers

Sort by: Most helpful
  1. S.Sengupta 24,201 Reputation points MVP
    2025-06-09T12:21:00.61+00:00

  2. Castorix31 90,191 Reputation points
    2025-06-09T14:12:53.8+00:00

    PDH return 0 on my PC for GPU (Intel HD GPU)

    But other APIs can be used, like D3DKMTQueryStatistics (+ GlobalMemoryStatusEx and GetSystemTimes)

    I asked ChatGPT to generate a Win32 app to format results and call APIs in a thread on a Button click, which seem correct on my Windows 10 OS (if I compare with Task Manager) =>

    
    #include <windows.h>
    #include <psapi.h>
    #include <dxgi1_4.h>
    #include <thread>
    #include <string>
    #include <chrono>
    #include <sstream>
    #include <iostream>
    
    #pragma comment(lib, "pdh.lib")
    #pragma comment(lib, "dxgi.lib")
    #pragma comment(lib, "user32.lib")
    #pragma comment(lib, "gdi32.lib")
    
    #include <d3dkmthk.h> 
    
    HWND hWnd;
    bool m_bRunning = false;
    LUID m_AdapterLuid = { 0, 0 };
    
    LUID GetAdapterLuid()
    {
        IDXGIFactory1* factory;
        CreateDXGIFactory1(IID_PPV_ARGS(&factory));
        IDXGIAdapter1* adapter;
        factory->EnumAdapters1(0, &adapter); // 0 = first adapter
        DXGI_ADAPTER_DESC1 desc;
        adapter->GetDesc1(&desc);
        return desc.AdapterLuid;
    }
    
    ULONGLONG prevTime = 0;
    
    float GetGPUUsage()
    {
        double usage = 0;
        D3DKMT_QUERYSTATISTICS stats = {};
        stats.Type = D3DKMT_QUERYSTATISTICS_NODE;
        stats.AdapterLuid = m_AdapterLuid;
        stats.QueryNode.NodeId = 0; // Node index, usually 0 for GPU
    
        NTSTATUS status = D3DKMTQueryStatistics(&stats);
        if (status == 0) 
        {
            ULONGLONG newTime = stats.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart;
            if (prevTime != 0)
            {
                ULONGLONG delta = newTime - prevTime;
                usage = (double)delta / 10000000.0 * 100.0;          
            }
            prevTime = newTime;
            return usage;
        }
        else
        {
            OutputDebugString(L"Failed to query D3DKMT statistics\n");
            return -1;
        }
    }
    
    void Log(const std::wstring& label, float value)
    {
        std::wstringstream ss;
        ss << label << L": " << value << L"%\n";
        OutputDebugString(ss.str().c_str());
    }
    
    float GetCPUUsage()
    {
        static ULARGE_INTEGER lastIdleTime = {}, lastKernelTime = {}, lastUserTime = {};
        FILETIME idleTime, kernelTime, userTime;
        if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
            return 0.0f;
    
        ULARGE_INTEGER idle, kernel, user;
        idle.LowPart = idleTime.dwLowDateTime;
        idle.HighPart = idleTime.dwHighDateTime;
    
        kernel.LowPart = kernelTime.dwLowDateTime;
        kernel.HighPart = kernelTime.dwHighDateTime;
    
        user.LowPart = userTime.dwLowDateTime;
        user.HighPart = userTime.dwHighDateTime;
    
        ULONGLONG sysIdleDiff = idle.QuadPart - lastIdleTime.QuadPart;
        ULONGLONG sysKernelDiff = kernel.QuadPart - lastKernelTime.QuadPart;
        ULONGLONG sysUserDiff = user.QuadPart - lastUserTime.QuadPart;
    
        lastIdleTime = idle;
        lastKernelTime = kernel;
        lastUserTime = user;
    
        ULONGLONG sysTotal = sysKernelDiff + sysUserDiff;
    
        if (sysTotal == 0)
            return 0.0f;
    
        // Normalize to percent of total logical cores
        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        int numProcs = sysInfo.dwNumberOfProcessors;
    
        double cpuUsage = 100.0 * (1.0 - (double)sysIdleDiff / sysTotal);
        cpuUsage /= numProcs; // Normalize
    
        // Clamp to 0-100%
        if (cpuUsage < 0.0) cpuUsage = 0.0;
        if (cpuUsage > 100.0) cpuUsage = 100.0;
    
        return static_cast<float>(cpuUsage);
    }
    
    float GetMemoryUsage()
    {
        MEMORYSTATUSEX memStatus = { sizeof(memStatus) };
        if (GlobalMemoryStatusEx(&memStatus))
            return (float)memStatus.dwMemoryLoad;
        return 0.0f;
    }
    
    void MonitorThread()
    {
        while (m_bRunning)
        {
            float cpu = GetCPUUsage();
            float mem = GetMemoryUsage();
            float gpu = GetGPUUsage();
    
            Log(L"CPU", cpu);
            Log(L"Memory", mem);
            Log(L"GPU", gpu);  
            OutputDebugString(L"\r\n");
    
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
    
    void StartMonitoring()
    {
        if (!m_bRunning) 
        {
            m_bRunning = true;
            std::thread(MonitorThread).detach();
        }
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        switch (msg) {
        case WM_DESTROY:
            m_bRunning = false;
            PostQuitMessage(0);
            break;
        case WM_COMMAND:
            if (LOWORD(wParam) == 1)
            {
                m_AdapterLuid = GetAdapterLuid();
                if (m_AdapterLuid.LowPart != 0 || m_AdapterLuid.HighPart != 0)            
                    StartMonitoring();
            }
            break;
        }
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
    {
        const wchar_t CLASS_NAME[] = L"PerfMonitorWindow";
        WNDCLASS wc = {};
        wc.lpfnWndProc = WndProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = CLASS_NAME;
        RegisterClass(&wc);
    
        hWnd = CreateWindowEx(0, CLASS_NAME, L"Win32 Perf Monitor",
            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 150,  nullptr, nullptr, hInstance, nullptr);
    
        CreateWindow(L"BUTTON", L"Start Monitor", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
            50, 50, 180, 30, hWnd, (HMENU)1, hInstance, nullptr);
        ShowWindow(hWnd, nCmdShow);
    
        MSG msg = {};
        while (GetMessage(&msg, nullptr, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return 0;
    }
    
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.