Get CPU Temperature
Asked Answered
P

5

36

I want to get the CPU temperature. Below is what I've done using C++ and WMI. I'm reading MSAcpi_ThermalZoneTemperature, but it's always the same and it's not the CPU temperature at all.

Is there any way to get the real temperature of the CPU without having to write drivers? Or are there any libs which I can use? Thank you in advance.

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

HRESULT GetCpuTemperature(LPLONG pTemperature)
{
        if (pTemperature == NULL)
                return E_INVALIDARG;

        *pTemperature = -1;
        HRESULT ci = CoInitialize(NULL);
        HRESULT hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
        if (SUCCEEDED(hr))
        {
                IWbemLocator *pLocator;
                hr = CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator);
                if (SUCCEEDED(hr))
                {
                        IWbemServices *pServices;
                        BSTR ns = SysAllocString(L"root\\WMI");
                        hr = pLocator->ConnectServer(ns, NULL, NULL, NULL, 0, NULL, NULL, &pServices);
                        pLocator->Release();
                        SysFreeString(ns);
                        if (SUCCEEDED(hr))
                        {
                                BSTR query = SysAllocString(L"SELECT * FROM MSAcpi_ThermalZoneTemperature");
                                BSTR wql = SysAllocString(L"WQL");
                                IEnumWbemClassObject *pEnum;
                                hr = pServices->ExecQuery(wql, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
                                SysFreeString(wql);
                                SysFreeString(query);
                                pServices->Release();
                                if (SUCCEEDED(hr))
                                {
                                        IWbemClassObject *pObject;
                                        ULONG returned;
                                        hr = pEnum->Next(WBEM_INFINITE, 1, &pObject, &returned);
                                        pEnum->Release();
                                        if (SUCCEEDED(hr))
                                        {
                                                BSTR temp = SysAllocString(L"CurrentTemperature");
                                                VARIANT v;
                                                VariantInit(&v);
                                                hr = pObject->Get(temp, 0, &v, NULL, NULL);
                                                pObject->Release();
                                                SysFreeString(temp);
                                                if (SUCCEEDED(hr))
                                                {
                                                        *pTemperature = V_I4(&v);
                                                }
                                                VariantClear(&v);
                                        }
                                }
                        }
                        if (ci == S_OK)
                        {
                                CoUninitialize();
                        }
                }
        }
        return hr;
}

int main(int argc, char **argv)
{
        LONG temp;
        GetCpuTemperature(&temp);
        printf("temp=%lf\n", ((double)temp / 10 - 273.15));
        getc(stdin);
        return 0;
}
Prog answered 26/4, 2014 at 18:24 Comment(6)
Did you compare your temperature with any monitoring software (superuser.com/a/395437/15484)? What about Kelvin-Celsius conversion? (PS: you may reuse driver from RealTemp or other project)Disbursement
I'm doing the conversion (temp / 10 - 273.15) but the result is always 70.05 and Core Temp shows different temperature.Prog
Have you taken a look at openhardwaremonitor.org ?Russia
Possible duplicate of How to get CPU temperature?Kilo
@Housy not a duplicate because this question is definitely about doing it via Windows API, while the one you link is about doing it via managed code.Dupaix
It is a duplicate. Proposed answers in the linked question point to either WMI (which is available via Windows API) or OpenHardwareMonitor (which is open-source and calls Windows API internally), it just needs some extra digging.Kilo
J
11

I must say this topic is a complete nightmare. It took me several weeks and several headaches researching it and trying different things before i got a working solution. Here is what i found:

Getting a sensor value from a piece of hardware requires direct access to that hardware via memory-mapped model-specific registers (MSRs), which not only are different in different CPUs but can only be done in a kernel (driver) and not in user-space application. Which is particularly difficult on Windows, where you need the drivers to be approved and signed by Microsoft.

Linux has such drivers and provides their output either via virtual files, like for example /sys/class/thermal/thermal_zone2/temp or more sophisticated command-line tools like lm-sensors.

Windows however is a wild-west in this and there is no universal and reliable way to do it. Here are the solutions i tried:

  1. The WMI method that is posted everywhere does not work on 90% of hardware, because it requires additional work from the manufacturers, which most often they won't do.

  2. Some hardware vendors provide their own SDK for reading CPU metrics, such as AMD Ryzen Master Monitoring SDK, but they are only for a specific CPU type and often outdated and not maintained.

  3. Some companies provide commercial multi-hardware SDKs, like this one, but they are paid and they have not even answered to any of my emails, so out of luck here too.

  4. There is an open-source project for monitoring hardware sensors called Open Hardware Monitor with several forks - the currently most active (in 2022) being Libre Hardware Monitor. It uses WinRing0 driver to access the hardware via their MSRs and extract the sensor values.
    It is not designed as a library but as a graphical application. However its installation comes with a DLL that exports the important functions.
    The only problem is it's written in C#, so it requires inter-language bindings to be usable from C++. One guy from the OpenRGB community made such bindings, it is available here. It's little difficult to make that build, but pre-built binaries can be found here.

So finally, using the lhwm-wrapper, which wraps LibreHardwareMonitor's DLL, which uses WinRing0 to read the sensors, i could write a user-space application that reads the temperature of my CPU, hooray!

Beware however that other applications installed in your computer might also be reading the sensor values in the background, and reading it too often will increase your CPU load and some hardware may not like it.
Which is the reason why later i decided to split my application into 1. a Windows service that reads the sensors only once per few seconds and publishes the results via a socket, and 2. the rest of the application that does the decisions based on the temperature.

Jampacked answered 14/6, 2022 at 10:40 Comment(2)
How did you get the temperature?Mylohyoid
@Mylohyoid Uhm, that's what the whole answer is about? Read point 4 and further.Jampacked
H
7

Truth to be told, it depends on the hardware.

A library that works on most hardware is OpenHardwareMonitorLib. Unfortunately, it has no documentation and actually doesn't really exist as an independent piece of software. It's part of an open source software named "Open Hardware Monitor". it's done in .NET C Sharp and of course, only works for Windows. Fortunately you can get it as a DLL, and the GUI is completely separated from the actual backend which is OpenHardwareMonitorLib.

Read this post on how to use it from C++

How to call a C# library from Native C++ (using C++\CLI and IJW)

So considering it has no docs it can be a bit hard to work with. After reading the source for a while this is my take:

using OpenHardwareMonitor.Hardware;
...
float? cpu_temperature_celcius = null;
Computer computer = new Computer();
computer.CPUEnabled = true;
computer.Open();
foreach (IHardware hardware in computer.Hardware)
    if (hardware.HardwareType == HardwareType.CPU)
        foreach (ISensor sensor in hardware.Sensors)
            if (sensor.SensorType == SensorType.Temperature)
                cpu_temperature_celcius = sensor.Value;

This C# code gets the temperature of the CPU in Celcius. Tested on an Intel Haswell CPU. It will most likely work for most other CPUs from AMD and Intel. OpenHardwareMonitorLib.dll is needed. You can compile it from source

You can get a lot of other information about the system with this library.

Note that the CPU of the user can have multiple temperature sensors. For example, a temperature sensor for every core, so don't always take the last one as I did in the example above.

Good luck.

Herl answered 29/7, 2019 at 16:22 Comment(0)
I
5

The link to source for OpenHardwareMonitorLib provided in Tomer's answer illustrates what has to happen at a low level to read this information out of different types of CPUs. For example, the IntelCPU class defines some model-specific registers:

private const uint IA32_THERM_STATUS_MSR = 0x019C;
private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
private const uint IA32_PERF_STATUS = 0x0198;
private const uint MSR_PLATFORM_INFO = 0xCE;
private const uint IA32_PACKAGE_THERM_STATUS = 0x1B1;
private const uint MSR_RAPL_POWER_UNIT = 0x606;
private const uint MSR_PKG_ENERY_STATUS = 0x611;
private const uint MSR_DRAM_ENERGY_STATUS = 0x619;
private const uint MSR_PP0_ENERY_STATUS = 0x639;
private const uint MSR_PP1_ENERY_STATUS = 0x641;

These are straight out of Intel's docs like CPU Monitoring with DTS/PECI (section 16 "Direct MSR Access"). They may also be documented in the Intel Software Developer Manual but I haven't checked.

The OpenHardwareMonitorLib uses Rdmsr and RdmsrTx to get temperature values out of the MSRs of interest.

The corresponding AMD code looks like it gets similar info out of a PCI register. AMD will have equivalent documentation somewhere that will define this.

In both cases these are, by definition, how the hardware exposes information about its temperature sensors. You can use a library like this and it'll be doing this under the hood, or you can write equivalent code of your own.

Instancy answered 1/8, 2019 at 17:12 Comment(1)
Note that rdmsr requires a kernel driver, that can't be done from user mode code.Aladdin
P
0

The mentioned WMI classes were not working for me in latest Windows 10. On my Dell-Laptop I could get the CPU-Temperature in Celsius here:

ROOT_CIMV2\Win32_PerfFormattedData_Counters_ThermalZoneInformation\HighPrecisionTemperature
Perot answered 6/3, 2021 at 8:29 Comment(0)
E
-1

WMI has the Win32_TemperatureProbe class:

http://msdn.microsoft.com/en-us/library/aa394493%28VS.85%29.aspx

Try it instead of MSAcpi_ThermalZoneTemperature

upd.

So, I tried the code from MS example page here. It shows the way to retrieve information from WMI classes.

It's generally the same as yours but the class name and property name. So change the line

BSTR query = SysAllocString(L"SELECT * FROM MSAcpi_ThermalZoneTemperature");

to

BSTR query = SysAllocString(L"SELECT * FROM Win32_TemperatureProbe");

or to it's parent class

BSTR query = SysAllocString(L"SELECT * FROM CIM_TemperatureSensor");

then change the property name to "CurrentReading"

But unfortunatelly the code for retrieving this parameter may be not implemented in motherboard drivers or MS drivers. In that case the result of VARIANT type will be set to NULL.

Ellon answered 26/4, 2014 at 19:42 Comment(4)
From those docs, Real-time readings for the CurrentReading property cannot be extracted from SMBIOS tables. For this reason, current implementations of WMI do not populate the CurrentReading property. Since op is looking for the current CPU temperature, I believe a working CurrentReading property would be necessary for this WMI class to do the job.Lactate
Is that working for you Monster? It doesn't work for me and as computerfreaker said, according to the documentation CurrentReading isn't populated.Prog
Yes, it works. Anyway, it's implemented in class CIM_TemperatureSensor too, try it.Ellon
I'm getting 0x80041010 error when I call IEnumWbemClassObject::Next(). Did anyone make any of his suggestions work?Brumby

© 2022 - 2024 — McMap. All rights reserved.