获取CPU温度

35

我想获取CPU温度。 以下是我使用C++和WMI所做的事情。我正在读取MSAcpi_ThermalZoneTemperature,但它始终相同,根本不是CPU温度。

是否有方法可以获取CPU的实际温度,而无需编写驱动程序?或者有可以使用的库吗?提前感谢您。

#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;
}

你有没有将自己的温度与任何监控软件(http://superuser.com/a/395437/15484)进行比较?开尔文-摄氏度转换怎么样?(附注:您可以重用RealTemp或其他项目中的驱动程序) - osgx
1
我正在进行转换 (temp / 10 - 273.15),但结果始终为70.05,而Core Temp显示的温度不同。 - Johnny Mnemonic
1
你看过 http://openhardwaremonitor.org/ 吗? - Rotem
可能是如何获取CPU温度?的重复问题。 - huysentruitw
@Housy 不是重复问题,因为这个问题明确涉及使用Windows API实现,而你提供的链接是关于使用托管代码实现的。 - Armen Michaeli
这是一个重复的问题。在链接的问题中,提出的答案指向了 Windows API 中可用的 WMI 或开源的 OpenHardwareMonitor(它在内部调用 Windows API),只需要进行一些额外的挖掘即可。 - huysentruitw
5个回答

7

我必须说这个话题是一个完全的噩梦。在我得到一个可行的解决方案之前,我花了几周时间和头痛研究它以及尝试不同的方法。以下是我找到的内容:

从硬件中获取传感器值需要通过内存映射特定于型号的寄存器 (MSRs) 直接访问该硬件,这不仅在不同的 CPU 中有所不同,而且只能在内核(驱动程序)中进行,而不能在用户空间应用程序中进行。这在 Windows 上尤其困难,因为您需要的驱动程序必须经过 Microsoft 的批准和签名。

Linux 有这样的驱动程序,并通过虚拟文件(例如/sys/class/thermal/thermal_zone2/temp)或更复杂的命令行工具(如lm-sensors)提供它们的输出。

然而,在 Windows 中,这是一个无序状态,没有通用和可靠的方法来实现它。以下是我尝试过的解决方案:

  1. 到处发布的WMI方法在90%的硬件上都不起作用,因为它需要厂商额外的工作,而大多数情况下他们不会这样做。

  2. 一些硬件供应商提供了自己的SDK来读取CPU指标,例如AMD Ryzen Master Monitoring SDK,但它们仅适用于特定的CPU类型,并且通常过时且未维护。

  3. 一些公司提供商业多硬件SDK,例如this one,但它们是付费的,而且他们甚至没有回复我的任何电子邮件,所以在这里也没办法。

  4. 有一个名为Open Hardware Monitor的开源项目用于监视硬件传感器,有几个分支 - 目前(2022年)最活跃的是Libre Hardware Monitor。它使用WinRing0驱动程序通过它们的MSRs访问硬件并提取传感器值。
    它不是设计为库,而是图形应用程序。然而,它的安装带有导出重要函数的DLL。
    唯一的问题是它是用C#编写的,所以需要跨语言绑定才能从C++中使用。OpenRGB社区的一个家伙制作了这样的绑定,它可以在here找到。它有点难以构建,但预构建的二进制文件可以在here找到。

最终,使用包装LibreHardwareMonitor的lhwm-wrapper, 该包装器使用WinRing0来读取传感器,我可以编写一个用户空间应用程序来读取我的CPU温度,太棒了!但是请注意,计算机中安装的其他应用程序也可能在后台读取传感器值,并且频繁读取会增加CPU负载,某些硬件可能不喜欢这样做。因此,我决定将我的应用程序分为以下两部分:1. Windows服务仅每隔几秒钟读取一次传感器并通过套接字发布结果,2. 其余部分根据温度做出决策。

7
说实话,这取决于硬件。
一个适用于大多数硬件的库是OpenHardwareMonitorLib。不幸的是,它没有文档,实际上也不存在独立的软件。它是一个名为“Open Hardware Monitor”的开源软件的一部分。它使用.NET C Sharp编写,当然,只适用于Windows。幸运的是,你可以将它作为DLL获取,并且GUI完全与实际的后端OpenHardwareMonitorLib分离。
阅读此帖子以了解如何从C++中使用它。 如何从本机C++(使用C++\CLI和IJW)调用C#库 因此,考虑到它没有文档,使用起来可能有些困难。经过一段时间的阅读源代码,这是我的看法:
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;

这段C#代码可以获取CPU的温度,单位为摄氏度。已在Intel Haswell CPU上测试过,大多数来自AMD和Intel的CPU也应该能使用。需要OpenHardwareMonitorLib.dll。您可以从source编译它。
使用这个库,您还可以获取系统的许多其他信息。
请注意,用户的CPU可能有多个温度传感器。例如,每个核心都有一个温度传感器,因此不要总是像我在上面的示例中那样取最后一个。
祝好运。

5
Tomer's answer提供的OpenHardwareMonitorLib源链接中,展示了从不同类型的CPU中读取此信息所必须发生的底层操作。例如,IntelCPU类定义了一些特定于模型的寄存器。
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;
这些内容直接来自英特尔的文档,例如使用DTS/PECI进行CPU监控(第16节“直接MSR访问”)。它们也可能在英特尔软件开发手册中有记录,但我还没有检查过。
OpenHardwareMonitorLib使用Rdmsr和RdmsrTx从感兴趣的MSR中获取温度值。

相应的AMD代码看起来从PCI寄存器中获取类似的信息。 AMD将在某个地方有等效的文档来定义这一点。

在这两种情况下,这些都是硬件如何公开其温度传感器信息的定义。您可以使用这样的库,并且它会在幕后执行此操作,或者您可以编写自己的等效代码。


4
请注意,“rdmsr”需要内核驱动程序才能使用,无法通过用户模式代码执行。 - josh poley

0

在最新的Windows 10中,我发现提到的WMI类无法正常工作。 在我的Dell笔记本电脑上,我可以在这里以摄氏度获取CPU温度:

ROOT_CIMV2\Win32_PerfFormattedData_Counters_ThermalZoneInformation\HighPrecisionTemperature

-1

WMI 有 Win32_TemperatureProbe 类:

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

尝试使用MSAcpi_ThermalZoneTemperature之外的方法

更新。

所以,我尝试了来自MS示例页面这里的代码。它展示了从WMI类中检索信息的方法。

基本上与你的代码相同,只是类名和属性名不同。所以修改这一行:

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

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

或者返回其父类

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

然后将属性名称更改为“CurrentReading”

但不幸的是,检索此参数的代码可能未在主板驱动程序或 MS 驱动程序中实现。在这种情况下,VARIANT 类型的结果将设置为 NULL。


3
从这些文档中可以得知,无法从SMBIOS表中提取当前读数属性的实时读数。因此,WMI的当前实现不会填充当前读数属性。由于操作者寻找当前CPU温度,我认为一个可用的“CurrentReading”属性对于这个WMI类来说是必要的。 - cf-
1
这对你有用吗,Monster?对我来说不起作用,正如computerfreaker所说,根据文档,CurrentReading没有被填充。 - Johnny Mnemonic
是的,它有效。无论如何,它也在CIM_TemperatureSensor类中实现了,请尝试一下。 - Monster
2
当我调用IEnumWbemClassObject :: Next()时,我遇到了0x80041010错误。有人能让他的建议起作用吗? - c00000fd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接