获取CPU温度

Joh*_*nic 25 c++ windows cpu winapi temperature

我想获得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;
}
Run Code Online (Sandbox Code Playgroud)

You*_*008 9

我必须说这个话题完全是一场噩梦。我花了几周的时间和几次头痛的时间来研究它并尝试不同的事情,然后才得到一个可行的解决方案。这是我发现的:

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

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

然而,Windows 在这方面是一个狂野西部,没有通用且可靠的方法来做到这一点。以下是我尝试过的解决方案:

  1. 随处可见的 WMI 方法不适用于 90% 的硬件,因为它需要制造商进行额外的工作,而制造商通常不会这样做。

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

  3. 有些公司提供商业多硬件 SDK,比如这个,但他们是付费的,甚至没有回复我的任何电子邮件,所以这里也很不幸。

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

最后,使用lhwm-wrapper(它包装了 LibreHardwareMonitor 的 DLL,它使用 WinRing0 来读取传感器),我可以编写一个用户空间应用程序来读取 CPU 的温度,万岁!

但请注意,计算机中安装的其他应用程序也可能在后台读取传感器值,并且过于频繁地读取它会增加您的 CPU 负载,并且某些硬件可能不喜欢它。
这就是为什么后来我决定将我的应用程序分成 1. 一个Windows 服务,每几秒只读取一次传感器并通过套接字发布结果,以及 2. 应用程序的其余部分根据温度。


tom*_*une 5

说实话,这取决于硬件。

适用于大多数硬件的库是 OpenHardwareMonitorLib。不幸的是,它没有文档,实际上并不是作为一个独立的软件而存在的。它是名为“Open Hardware Monitor”的开源软件的一部分。它是在 .NET C Sharp 中完成的,当然,仅适用于 Windows。幸运的是,您可以将其作为 DLL 获取,并且 GUI 与实际的后端 OpenHardwareMonitorLib 完全分离。

阅读这篇关于如何从 C++ 使用它的文章

如何从本机 C++ 调用 C# 库(使用 C++\CLI 和 IJW)

因此,考虑到它没有文档,这可能有点难以使用。阅读源代码一段时间后,这是我的看法:

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;
Run Code Online (Sandbox Code Playgroud)

此#C 代码已确认可成功获取 Celcius 中 Intel Haswell CPU 的温度。它很可能适用于 AMD 和 Intel 的大多数其他 CPU。需要 OpenHardwareMonitorLib.dll。您可以从源代码编译它

您可以使用此库获取有关系统的许多其他信息。

请注意,用户的 CPU 可以有多个温度传感器。例如,每个核心都有一个温度传感器,所以不要像我在上面的例子中那样总是使用最后一个。

祝你好运。


Aar*_*man 5

Tomer 的回答中提供的 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;
Run Code Online (Sandbox Code Playgroud)

这些直接来自 Intel 的文档,例如CPU Monitoring with DTS/PECI(第 16 节“直接 MSR 访问”)。它们也可能记录在英特尔软件开发人员手册中,但我没有检查。

OpenHardwareMonitorLib 使用 Rdmsr 和 RdmsrTx 从感兴趣的 MSR 中获取温度值。

相应的AMD代码看起来像它得到了一个PCI寄存器的类似信息。AMD 将在某处提供等效的文档来定义这一点。

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

  • 请注意,“rdmsr”需要内核驱动程序,这无法从用户模式代码中完成。 (3认同)

Mon*_*ter -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");
Run Code Online (Sandbox Code Playgroud)

BSTR query = SysAllocString(L"SELECT * FROM Win32_TemperatureProbe");
Run Code Online (Sandbox Code Playgroud)

或者它的父类

BSTR query = SysAllocString(L"SELECT * FROM CIM_TemperatureSensor");
Run Code Online (Sandbox Code Playgroud)

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

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

  • 从这些文档中,*无法从 SMBIOS 表中提取 CurrentReading 属性的实时读数。因此,WMI 的当前实现不会填充 CurrentReading 属性。* 由于 op 正在查找当前 CPU 温度,我相信该 WMI 类需要一个有效的“CurrentReading”属性来完成这项工作。 (3认同)
  • 当我调用“IEnumWbemClassObject::Next()”时,出现“0x80041010”错误。有人让他的建议奏效吗? (2认同)