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)
我必须说这个话题完全是一场噩梦。我花了几周的时间和几次头痛的时间来研究它并尝试不同的事情,然后才得到一个可行的解决方案。这是我发现的:
从硬件获取传感器值需要通过内存映射的模型特定寄存器(MSR) 直接访问该硬件,这不仅在不同的 CPU 中有所不同,而且只能在内核(驱动程序)中完成,而不能在用户中完成-空间应用。这在 Windows 上尤其困难,因为您需要驱动程序得到 Microsoft 的批准和签名。
Linux 具有此类驱动程序,并通过虚拟文件(例如lm-sensors/sys/class/thermal/thermal_zone2/temp
等更复杂的命令行工具)提供输出。
然而,Windows 在这方面是一个狂野西部,没有通用且可靠的方法来做到这一点。以下是我尝试过的解决方案:
随处可见的 WMI 方法不适用于 90% 的硬件,因为它需要制造商进行额外的工作,而制造商通常不会这样做。
一些硬件供应商提供了自己的 SDK 来读取 CPU 指标,例如AMD Ryzen Master Monitoring SDK,但它们仅适用于特定的 CPU 类型,并且通常已经过时且未维护。
有些公司提供商业多硬件 SDK,比如这个,但他们是付费的,甚至没有回复我的任何电子邮件,所以这里也很不幸。
有一个用于监控硬件传感器的开源项目,称为Open Hardware Monitor,有多个分支 - 目前最活跃的项目(2022 年)是Libre Hardware Monitor。它使用 WinRing0 驱动程序通过 MSR 访问硬件并提取传感器值。
它不是一个库,而是一个图形应用程序。然而,它的安装附带了一个导出重要函数的 DLL。
唯一的问题是它是用 C# 编写的,因此需要跨语言绑定才能从 C++ 中使用。OpenRGB 社区的一个人制作了这样的绑定,可以在这里找到。进行该构建并不困难,但可以在此处找到预构建的二进制文件。
最后,使用lhwm-wrapper(它包装了 LibreHardwareMonitor 的 DLL,它使用 WinRing0 来读取传感器),我可以编写一个用户空间应用程序来读取 CPU 的温度,万岁!
但请注意,计算机中安装的其他应用程序也可能在后台读取传感器值,并且过于频繁地读取它会增加您的 CPU 负载,并且某些硬件可能不喜欢它。
这就是为什么后来我决定将我的应用程序分成 1. 一个Windows 服务,每几秒只读取一次传感器并通过套接字发布结果,以及 2. 应用程序的其余部分根据温度。
说实话,这取决于硬件。
适用于大多数硬件的库是 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 可以有多个温度传感器。例如,每个核心都有一个温度传感器,所以不要像我在上面的例子中那样总是使用最后一个。
祝你好运。
Tomer 的回答中提供的 OpenHardwareMonitorLib 源链接说明了从不同类型的 CPU 读取此信息必须在低级别发生的事情。例如,IntelCPU类定义了一些特定于模型的寄存器:
Run Code Online (Sandbox Code Playgroud)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;
这些直接来自 Intel 的文档,例如CPU Monitoring with DTS/PECI(第 16 节“直接 MSR 访问”)。它们也可能记录在英特尔软件开发人员手册中,但我没有检查。
OpenHardwareMonitorLib 使用 Rdmsr 和 RdmsrTx 从感兴趣的 MSR 中获取温度值。
将相应的AMD代码看起来像它得到了一个PCI寄存器的类似信息。AMD 将在某处提供等效的文档来定义这一点。
在这两种情况下,根据定义,这些都是硬件如何公开有关其温度传感器的信息。您可以使用这样的库,它会在后台执行此操作,或者您可以编写自己的等效代码。
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。
归档时间: |
|
查看次数: |
17539 次 |
最近记录: |