VC++实现Windows下CPU多核温度监控:WMI与MSR方案详解
1. 项目概述与核心价值最近在折腾一个性能监控工具需要实时获取CPU的温度数据特别是现在动辄十几个核心的处理器每个核心的温度都可能不一样。网上找了一圈发现用VC来做这个事情的完整、靠谱的教程还真不多要么是只讲单核要么是代码跑不通。正好借着这个机会我把从底层原理到代码实现的完整链路都梳理了一遍踩过的坑和验证过的方案都写在这里。如果你也在用Visual Studio做Windows下的硬件监控、性能分析或者超频工具开发这篇内容应该能帮你省下不少折腾的时间。简单来说我们要做的事情就是在Windows操作系统上使用Visual C也就是我们常说的VC编写一个程序能够准确地读取到CPU每一个物理核心的当前温度。这听起来像是驱动开发或者硬件厂商SDK的活儿但实际上通过一些系统提供的接口和技巧我们完全可以在用户态的应用程序里实现它。这个功能的价值很明显对于开发系统监控软件、游戏内叠加显示如MSI Afterburner的OSD、或者需要根据温度动态调整算力的专业应用如渲染、科学计算来说都是非常核心的基础能力。2. 核心原理与方案选型为什么读取CPU温度不像读个文本文件那么简单因为温度传感器Thermal Sensor是直接集成在CPU芯片内部的硬件单元它的数据并不存在于某个我们熟知的文件或注册表里。操作系统特别是Windows为了安全性和稳定性不会允许普通的应用程序直接去读写CPU的硬件寄存器。这就需要一个“中间人”。目前主流的有三种技术路径各有优劣我挨个分析一下。2.1 WMIWindows Management Instrumentation方案这是最“正统”、最面向应用层开发者的方法。WMI是Windows系统管理的基石它提供了一个统一的接口来访问和管理系统资源其中就包括硬件传感器信息。具体的类位于ROOT\CIMV2命名空间下例如Win32_TemperatureProbe或MSAcpi_ThermalZoneTemperature。优点 纯软件接口无需额外驱动兼容性好理论上支持所有实现了WMI接口的硬件/驱动代码相对清晰。缺点信息粒度粗且严重依赖主板厂商和驱动。很多主板/CPU的WMI温度探头只提供一个“CPU”总体温度或者提供的是CPU插槽附近的温度Socket Temperature而不是每个核心的独立温度。对于多核CPU的精细监控这个方案往往不够用。此外不同品牌主板如华硕、微星、技嘉的WMI实现可能不同导致查询语句不稳定。2.2 WinRing0 或 OpenHardwareMonitorLib 等第三方库方案这是一条捷径。有一些开源项目比如OpenHardwareMonitor它内部实现了一套跨平台的硬件监控库。其Windows版本的核心通常是基于一个叫WinRing0的驱动程序来直接访问硬件端口。你的应用程序通过调用这些库提供的API就能轻松获取到详细的温度、电压、风扇转速等信息。优点简单、快速、功能全面。通常一行代码就能拿到结构化的所有传感器数据包括每个核心的温度。OpenHardwareMonitorLib的接口设计得不错。缺点引入了第三方依赖和潜在的法律、安全风险。WinRing0驱动需要以高权限通常是管理员权限加载并且会被部分安全软件视为风险行为。在商业软件中使用需要仔细考虑许可证问题。此外库的更新可能滞后于新硬件。2.3 通过内核模式驱动程序通信如使用DeviceIoControl这是最底层、最灵活也是性能最好的方案。CPU厂商主要是Intel和AMD会提供访问其MSRModel-Specific Register型号特定寄存器或通过ACPI高级配置与电源管理接口的ECEmbedded Controller嵌入式控制器空间来读取温度数据的规范。主板厂商或操作系统如Windows的ACPI.sys会实现一个内核模式的驱动程序暴露一个设备对象Device Object。我们的用户态程序通过标准的CreateFile打开这个设备然后用DeviceIoControl发送特定的控制代码IOCTL来交换数据。优点官方或半官方路径效率高能获取最原始、最精确的数据。Intel的DTSDigital Thermal Sensor和AMD的传感器数据都可以通过这种方式读取。这是许多专业监控软件如HWiNFO64、Core Temp采用的方案。缺点实现复杂需要深入了解硬件规范和Windows驱动通信模型。不同代际、不同型号的CPU其MSR地址和算法可能不同需要维护一个庞大的配置表。直接与未公开文档的ACPI设备通信也可能存在稳定性风险。我的选择与理由 对于追求稳定、可控且希望深入理解原理的实战项目我推荐第三种方案的简化版本即寻找并利用系统中已经存在的、相对稳定的ACPI温度设备。这个方案平衡了难度和效果。我们不需要自己写驱动而是去“询问”系统里现成的驱动。接下来我将重点围绕这个方案展开详细说明如何找到它、如何与它通信并处理多核的情况。3. 实战环境准备与关键工具工欲善其事必先利其器。在开始写代码之前我们需要准备好环境和侦察工具。3.1 开发环境配置IDE Visual Studio 2019 或 2022。社区版即可。确保安装时勾选了“使用C的桌面开发”工作负载。项目类型 创建一个新的“控制台应用Console App”项目。这样方便我们快速打印调试信息。Windows SDK 使用你安装的Visual Studio所附带的最新Windows SDK即可。我们的代码会用到一些基本的Windows API。权限 后续操作需要管理员权限。可以在Visual Studio中设置项目属性在“链接器”-“清单文件”-“UAC执行级别”中设置为requireAdministrator这样每次调试运行时都会自动请求提权。3.2 侦察系统ACPI温度设备我们的目标是找到系统里那个能报告CPU温度的ACPI设备。这里隆重推荐两个“侦察兵”工具RWEverything 这是一款强大的硬件信息查看工具可以读取物理内存、IO端口、PCI配置空间、MSR寄存器等。我们将用它来初步探测ACPI EC空间寻找温度相关的数据。ACPIView或Windows SDK 中的 ACPI 示例工具 更专业的工具是微软WDKWindows Driver Kit里提供的AcpiView工具。它可以解析并显示系统的整个ACPI表包括DSDTDifferentiated System Description Table差分系统描述表这是描述所有硬件设备和方法的“总说明书”。侦察步骤实录首先以管理员身份运行RWEverything。点击菜单Access-ACPI-EC。这会打开嵌入式控制器的数据空间。不同主板的布局天差地别你需要结合主板手册如果有或者通过经验来猜测。温度数据通常存放在连续的几个字节中。一个笨办法是同时打开一个你知道的监控软件如HWiNFO观察某个核心温度变化时RWEverything里哪个字节的数值在同步变化。注意此操作有风险不要随意写入数据更安全的方法是使用ACPIView。运行acpiview命令它会输出巨量的信息。你需要搜索ThermalZone关键字。一个ThermalZone对象代表一个温度监控区域。你会看到类似_TZ.THRM这样的对象以及它下面关联的方法比如_TMP读温度。_TMP方法的返回值就是该温区的温度单位是十分之一开尔文deci-Kelvin。通过以上工具你可以确认你的系统是否存在标准的ACPI温度接口。对于现代CPU更常见的是通过MSR读取这需要另一套方法。为了教程的通用性我们将采用一种更“迂回”但兼容性可能更好的方式通过WMI来访问ACPI命名空间中的特定方法。这本质上还是方案三只是使用了WMI作为通信协议。4. 核心代码实现与分步解析假设我们已经通过侦察确定了一个ACPI路径比如ACPI\ThermalZone\TZ00_0。我们的代码将围绕与这个设备通信展开。以下是完整的实现步骤和代码解析。4.1 初始化COM库与WMI连接WMI基于COM技术所以第一步必须初始化COM库。#include Windows.h #include comdef.h #include Wbemidl.h #pragma comment(lib, wbemuuid.lib) // 链接WMI库 int main() { HRESULT hres; // 1. 初始化COM多线程模型 hres CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { std::cerr 初始化COM库失败。错误码: 0x std::hex hres std::endl; return 1; } // 2. 设置COM安全级别进程级别 hres CoInitializeSecurity( NULL, -1, // 使用默认验证服务 NULL, // 使用默认授权服务 NULL, // 保留必须为NULL RPC_C_AUTHN_LEVEL_DEFAULT, // 默认身份验证级别 RPC_C_IMP_LEVEL_IMPERSONATE, // 模拟级别允许服务器代表客户端操作 NULL, // 使用默认身份验证信息 EOAC_NONE, // 无额外功能 NULL // 保留 ); if (FAILED(hres) hres ! RPC_E_TOO_LATE) { // RPC_E_TOO_LATE 表示已初始化可忽略 CoUninitialize(); std::cerr 设置安全级别失败。错误码: 0x std::hex hres std::endl; return 1; } // 3. 创建WMI连接器 (IWbemLocator) 并连接到命名空间 IWbemLocator* pLoc NULL; hres CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)pLoc ); if (FAILED(hres)) { CoUninitialize(); std::cerr 创建WMI定位器失败。错误码: 0x std::hex hres std::endl; return 1; } IWbemServices* pSvc NULL; // 连接到本地计算机的ROOT\WMI命名空间。ACPI相关的方法常在此或ROOT\CIMV2下。 // 有时温度数据在 ROOT\WMI 下的 WmiMonitorBrightness? 不那是显示器。 // 对于ACPI ThermalZone我们尝试连接 ROOT\WMI 或 ROOT\CIMV2。 hres pLoc-ConnectServer( _bstr_t(LROOT\\WMI), // 命名空间路径 NULL, // 用户名NULL表示当前用户 NULL, // 密码 0, // 区域设置 NULL, // 安全标志 0, // 授权 0, // 上下文指针 pSvc // 返回的IWbemServices指针 ); if (FAILED(hres)) { pLoc-Release(); CoUninitialize(); std::cerr 连接WMI命名空间失败。错误码: 0x std::hex hres std::endl; return 1; } // 4. 设置代理安全级别接口级别 hres CoSetProxyBlanket( pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); if (FAILED(hres)) { pSvc-Release(); pLoc-Release(); CoUninitialize(); std::cerr 设置代理安全级别失败。错误码: 0x std::hex hres std::endl; return 1; } std::cout WMI连接初始化成功。 std::endl; // ... 后续查询代码将写在这里 // 5. 清理资源 pSvc-Release(); pLoc-Release(); CoUninitialize(); return 0; }注意连接WMI命名空间可能需要管理员权限。如果连接ROOT\WMI失败可以尝试ROOT\CIMV2。不同的硬件厂商可能会将温度设备注册在不同的命名空间下。4.2 枚举系统内的ACPI ThermalZone设备在连接成功后我们需要找出所有代表温度区域的WMI对象。// 接上面的代码在清理资源之前 IEnumWbemClassObject* pEnumerator NULL; // 使用WQL查询语言查找所有的MSAcpi_ThermalZoneTemperature实例 // 注意类名可能因系统而异也可能是 Win32_TemperatureProbe 或 Acpi_ThermalZone hres pSvc-ExecQuery( bstr_t(WQL), bstr_t(SELECT * FROM MSAcpi_ThermalZoneTemperature), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, pEnumerator ); if (FAILED(hres)) { std::cerr WMI查询失败。错误码: 0x std::hex hres std::endl; // 尝试另一个常见的类名 hres pSvc-ExecQuery( bstr_t(WQL), bstr_t(SELECT * FROM Acpi_ThermalZone), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, pEnumerator ); if (FAILED(hres)) { std::cerr 备用查询也失败。可能不支持或权限不足。 std::endl; pSvc-Release(); pLoc-Release(); CoUninitialize(); return 1; } } std::cout 开始枚举ThermalZone设备... std::endl; IWbemClassObject* pclsObj NULL; ULONG uReturn 0; int zoneCount 0; while (pEnumerator) { hres pEnumerator-Next(WBEM_INFINITE, 1, pclsObj, uReturn); if (uReturn 0) { break; // 没有更多对象了 } VARIANT vtProp; VariantInit(vtProp); // 获取设备实例的路径Key用于后续调用方法 hres pclsObj-Get(L__PATH, 0, vtProp, 0, 0); if (SUCCEEDED(hres) (vtProp.vt VT_BSTR)) { std::wcout L找到ThermalZone [ zoneCount L]: vtProp.bstrVal std::endl; // 将设备路径保存下来后面用于查询温度 // 这里可以存入一个vectorBSTR以备后用 } VariantClear(vtProp); // 尝试获取当前温度值如果该类直接提供属性 hres pclsObj-Get(LCurrentTemperature, 0, vtProp, 0, 0); if (SUCCEEDED(hres)) { if (vtProp.vt VT_I4) { // MSAcpi_ThermalZoneTemperature 的温度单位是 0.1 Kelvin double tempCelsius (vtProp.intVal / 10.0) - 273.15; std::cout 当前温度: tempCelsius °C std::endl; } } else { // 如果CurrentTemperature属性不存在可能需要调用_TMP方法 std::cout 未找到直接温度属性可能需要调用方法。 std::endl; } VariantClear(vtProp); pclsObj-Release(); zoneCount; } if (zoneCount 0) { std::cout 未找到任何ACPI ThermalZone设备。这可能意味着 std::endl; std::cout 1. 主板/BIOS未暴露标准ACPI温度接口。 std::endl; std::cout 2. 需要尝试连接 ROOT\\CIMV2 命名空间查询 Win32_TemperatureProbe。 std::endl; std::cout 3. 必须使用更底层的MSR读取方案。 std::endl; } pEnumerator-Release();这段代码的核心是ExecQuery和遍历枚举器。它首先尝试查询MSAcpi_ThermalZoneTemperature类这是较新的Windows系统Vista之后引入的用于替代旧的Win32_TemperatureProbe。如果找不到再尝试Acpi_ThermalZone。找到的每个实例都代表一个温度监控点。4.3 调用_TMP方法获取精确温度很多时候WMI类并不直接提供CurrentTemperature属性或者提供的属性不准确。这时我们需要调用ACPI对象的标准方法_TMPThermal Temperature。这需要通过WMI的ExecMethod功能来实现。假设我们已经从上面获取了一个设备的路径存储在bstr_t objPath中。// 假设 pSvc 是已连接的IWbemServices objPath 是目标设备的路径BSTR IWbemClassObject* pClass NULL; IWbemClassObject* pInParamsDefinition NULL; IWbemClassObject* pInParams NULL; IWbemClassObject* pOutParams NULL; // 1. 获取方法定义_TMP hres pSvc-GetObject(_bstr_t(LMSAcpi_ThermalZoneTemperature), 0, NULL, pClass, NULL); if (FAILED(hres)) { // 如果类名不对尝试获取 Acpi_ThermalZone hres pSvc-GetObject(_bstr_t(LAcpi_ThermalZone), 0, NULL, pClass, NULL); } if (SUCCEEDED(hres)) { hres pClass-GetMethod(_bstr_t(L_TMP), 0, pInParamsDefinition, NULL); } // 2. 创建方法的输入参数实例_TMP通常无输入参数 if (SUCCEEDED(hres)) { hres pInParamsDefinition-SpawnInstance(0, pInParams); } // 3. 执行方法 if (SUCCEEDED(hres)) { hres pSvc-ExecMethod( objPath, // 对象路径 _bstr_t(L_TMP), // 方法名 0, NULL, pInParams, // 输入参数此处为NULL或无参 pOutParams, // 输出参数 NULL ); } // 4. 解析输出参数 if (SUCCEEDED(hres) pOutParams) { VARIANT vtRetVal; VariantInit(vtRetVal); // 输出参数中返回值通常在一个叫 ReturnValue 的属性里 hres pOutParams-Get(_bstr_t(LReturnValue), 0, vtRetVal, NULL, 0); if (SUCCEEDED(hres) (vtRetVal.vt VT_I4)) { // 同样单位是 0.1 Kelvin double tempCelsius (vtRetVal.intVal / 10.0) - 273.15; std::cout 通过_TMP方法读取的温度: tempCelsius °C std::endl; } else { // 有时返回值直接就是温度属性名如 CurrentTemperature hres pOutParams-Get(_bstr_t(LCurrentTemperature), 0, vtRetVal, NULL, 0); if (SUCCEEDED(hres) (vtRetVal.vt VT_I4)) { double tempCelsius (vtRetVal.intVal / 10.0) - 273.15; std::cout 从输出参数中读取的温度: tempCelsius °C std::endl; } } VariantClear(vtRetVal); pOutParams-Release(); } // 5. 清理 if (pInParams) pInParams-Release(); if (pInParamsDefinition) pInParamsDefinition-Release(); if (pClass) pClass-Release();重要提示调用_TMP方法并非总是有效这完全取决于主板BIOS的ACPI表实现是否规范。这是此方案最大的不确定性所在。4.4 处理多核CPU与核心温度映射通过上述ACPI WMI方法获取的温度很可能只是一个“封装温度”Package Temperature或者一个主要核心的温度而不是每个独立核心的温度。要获取每个核心的温度通常必须诉诸于读取MSRModel Specific Register。Intel CPU从Core架构开始每个核心都有一个数字热传感器Digital Thermal Sensor, DTS其温度值存放在一个特定的MSR中IA32_THERM_STATUS MSR, 地址为0x19C。AMD CPU也有类似的寄存器例如HWCR和THERMTRIP_STATUS等但地址和算法不同。核心思路加载内核驱动 用户态程序无法直接执行RDMSR读MSR指令这需要内核权限。因此我们需要一个简单的、有签名的驱动程序来协助。这就是为什么WinRing0或OpenHardwareMonitorLib这类库内部都包含一个驱动。通过DeviceIoControl通信 我们的应用程序通过CreateFile打开驱动创建的设备然后使用DeviceIoControl发送一个自定义的IOCTL控制码。驱动收到这个请求后在内核态执行RDMSR指令将结果通过同一个IOCTL返回给应用程序。解析温度值 从MSR中读出的64位值中特定的位域bits 22:16代表的是“距TjMax的Delta值”即离最高结温还有多少度。我们需要知道该CPU的TjMax值通常为100°C或105°C然后用TjMax - Delta得到当前温度。由于编写和签名驱动涉及复杂的Windows驱动开发WDK和代码签名证书这超出了单篇教程的范围。一个折中的实战建议是使用一个已经验证过的、开源的、仅用于读取的轻量级驱动。例如WinRing0的驱动WinRing0x64.sys或OpenHardwareMonitor项目中的驱动。你可以研究其IOCTL接口然后在自己的程序中调用。简化版多核温度读取伪代码逻辑// 假设我们已经通过某种方式如加载WinRing0驱动获得了读取MSR的能力 for (int core 0; core totalCores; core) { // 1. 将线程亲和性设置为指定核心确保RDMSR指令在该核心上执行 SetThreadAffinityMask(GetCurrentThread(), 1ULL core); Sleep(1); // 等待一个时间片切换确保线程真的跑在目标核心上 // 2. 通过驱动接口读取该核心的 IA32_THERM_STATUS MSR (0x19C) uint64_t msrValue ReadMsr(0x19C, core); // 这个函数内部通过DeviceIoControl与驱动通信 // 3. 解析温度Delta值 (bits 22:16) int delta (msrValue 16) 0x7F; // 4. 计算温度 (假设TjMax 100) double tempCelsius 100.0 - delta; std::cout Core core Temperature: tempCelsius °C std::endl; } // 恢复线程亲和性 SetThreadAffinityMask(GetCurrentThread(), 0xFFFFFFFF);关键点SetThreadAffinityMask是必须的因为MSR是每个核心独立的寄存器。在一个核心上无法读取另一个核心的MSR。ReadMsr函数是你需要实现的核心它封装了与内核驱动的通信。TjMax值并非总是100。对于某些CPU特别是移动版或更新的架构需要从另一个MSRIA32_TEMPERATURE_TARGET, 地址0x1A2中读取。这增加了复杂性。5. 常见问题、排查技巧与优化建议在实际编码和测试过程中你几乎一定会遇到下面这些问题。这里是我踩坑后的经验总结。5.1 WMI查询返回空或访问被拒绝现象ExecQuery返回WBEM_E_ACCESS_DENIED或枚举结果为空。排查权限 确保程序以管理员身份运行。这是最常见的原因。命名空间 尝试更换命名空间。ROOT\WMI、ROOT\CIMV2、ROOT\Hardware都可能藏有硬件信息。使用wbemtest工具系统自带可以图形化地浏览所有命名空间和类。类名 类名可能不标准。用wbemtest连接后在指定命名空间下查看所有类搜索Thermal、Temperature、Acpi等关键词。WMI服务 检查Windows Management Instrumentation服务是否正在运行。5.2 读取的温度值异常如-1 0 或极大值现象 温度值是-1、0、65535等明显不合理的数据。排查单位转换错误 确认温度值的单位。ACPI标准是0.1开尔文需要val/10 - 273.15。MSR读出的是Delta值需要TjMax - delta。无效数据 传感器可能未初始化或不可用。WMI属性可能返回一个uint32的最大值 (0xFFFFFFFF) 表示无效。读取MSR前需要检查IA32_THERM_STATUS的VALID位bit 31是否为1。错误的传感器 你读到的可能不是CPU核心温度而是主板、芯片组或其他设备的温度。仔细检查WMI对象的Name、Description或DeviceID属性。5.3 如何区分多个温度传感器对应哪个CPU核心这是最棘手的问题。ACPI ThermalZone 或 WMI 对象通常没有“Core 0”、“Core 1”这样的友好名称。策略一ACPI 在DSDT表中ThermalZone对象可能通过_PSV、_ACx等方法关联到特定的处理器对象Processor。解析DSDT需要专业的ACPI知识几乎不可行。策略二MSR这是唯一可靠的方法。通过将线程绑定到特定核心并读取该核心的MSR可以明确无误地获得该核心的温度。这就是为什么专业工具都采用MSR方案。策略三试探法 如果你通过WMI找到多个温度值可以同时运行一个单核心压力测试如Prime95指定线程亲和性观察哪个温度值飙升来粗略对应。但这不精确且无法自动化。5.4 程序在部分电脑上崩溃或无数据原因 硬件和BIOS的多样性是最大的挑战。你的代码可能基于一种特定的ACPI实现或CPU型号。应对增强健壮性 所有COM、WMI API调用都必须检查HRESULT返回值。Variant类型在使用前后必须用VariantInit和VariantClear妥善管理。降级方案 实现一个“探测-回退”机制。先尝试MSR方案需驱动如果失败如驱动加载失败则回退到WMI ACPI方案再失败则尝试WMIWin32_TemperatureProbe最后可以提供一个“未找到传感器”的友好提示。收集信息 在程序日志中记录探测到的WMI类名、对象路径、返回值这有助于远程诊断用户的问题。5.5 性能与实时性考量频繁读取 不要在一个紧密循环中比如每秒几十次频繁调用WMI或通过驱动读取MSR。WMI调用开销较大而频繁的DeviceIoControl上下文切换也会消耗资源。建议将读取间隔设置在500ms到1s之间对于温度监控完全足够。缓存连接 WMI的IWbemServices连接可以缓存起来在整个程序生命周期内复用而不是每次读取都重新连接和初始化COM。事件通知 高级用法是使用WMI的异步通知或事件订阅IWbemObjectSink当温度超过阈值时接收事件而不是轮询。但这实现起来更复杂。6. 一个完整的、健壮的多方案读取示例框架结合以上所有内容一个工业级可用的温度读取模块应该如下设计class CPUTemperatureMonitor { public: CPUTemperatureMonitor(); ~CPUTemperatureMonitor(); bool Initialize(); // 尝试多种初始化方案 std::vectordouble GetAllCoreTemperatures(); // 获取所有核心温度 double GetPackageTemperature(); // 获取封装温度 private: enum class MonitorType { Unknown, WMI_AcpiThermalZone, WMI_MsAcpiThermal, WinRing0_Msr, // 使用WinRing0驱动读MSR OpenHardwareMonitorLib // 使用OHM库 }; MonitorType m_type; // ... 各种方案所需的句柄、接口指针等成员变量 bool TryInitializeWMI(); bool TryInitializeWinRing0(); std::vectordouble ReadTemperaturesViaWMI(); std::vectordouble ReadTemperaturesViaMSR(); };在Initialize()函数中按优先级尝试不同方案首先检查是否可以将WinRing0或OpenHardwareMonitor的驱动作为备选方案加载需考虑许可证和分发。如果不行则尝试WMI方案遍历所有可能的命名空间和类名。初始化成功后根据m_type调用对应的读取函数。在GetAllCoreTemperatures()中如果是MSR方案就遍历所有核心绑定亲和性后读取如果是WMI方案则返回找到的所有温度值列表并注明可能无法与核心一一对应。最后分享一个我个人的深刻体会在Windows下做硬件直访永远要把兼容性和优雅降级放在第一位。你永远不知道用户的主板BIOS程序员那天是不是心情不好。因此清晰的日志、多套备选方案、以及最终向用户坦诚“无法获取某些数据”的能力比强行实现一个在80%机器上能跑但20%会崩溃的功能要重要得多。这个项目最好的结果不是代码多优雅而是它能稳定、安静地在各种奇怪的电脑上提供尽可能准确的数据。

相关新闻

从VS2015迁移到VSCode:开发思维转变与实战配置指南

从VS2015迁移到VSCode:开发思维转变与实战配置指南

1. 从“巨舰”到“快艇”:一次开发思维的转变 如果你和我一样,在Visual Studio 2015(VS2015)那个“全家桶”式的集成开发环境里深耕了多年,突然转向Visual Studio Code(VSCode),最初…

2026/8/11 5:57:00 阅读更多 →
ROS2测试版功能包清单获取与风险评估实战指南

ROS2测试版功能包清单获取与风险评估实战指南

1. 项目缘起:为什么需要一份“测试版”功能包清单? 作为一名长期在机器人操作系统(ROS)领域摸爬滚打的开发者,我深知每一次大版本升级都像是一次“搬家”。从ROS1到ROS2,从Foxy到Galactic,再到如…

2026/8/11 5:57:00 阅读更多 →
SARscape D-InSAR哨兵1数据处理全流程与形变监测实战指南

SARscape D-InSAR哨兵1数据处理全流程与形变监测实战指南

1. 项目概述:从雷达卫星数据到地表形变图如果你手头有一组时间序列的哨兵1号雷达卫星影像,想知道这段时间里地面有没有发生沉降、滑坡或者火山隆起,那么D-InSAR(差分合成孔径雷达干涉测量)技术就是你最得力的工具。它就…

2026/8/11 5:57:00 阅读更多 →

最新新闻

ReAct架构解析:大语言模型如何通过思考与行动循环构建智能体

ReAct架构解析:大语言模型如何通过思考与行动循环构建智能体

1. 从“指令-响应”到“思考-行动”的范式跃迁如果你在过去一年里尝试过构建或使用智能体,大概率经历过这样的场景:你给一个基于大语言模型的智能体下达一个指令,比如“帮我查一下今天北京的天气,然后告诉我是否需要带伞”&#x…

2026/8/11 6:49:17 阅读更多 →
复杂任务流程设计:如何在约束条件下构建稳定可靠的长串操作

复杂任务流程设计:如何在约束条件下构建稳定可靠的长串操作

1. 先搞清楚“超绝平衡木上法长串”到底指什么看到“超绝平衡木上法长串”这个标题,第一反应可能是体操或杂技里的高难度动作组合。但在技术圈,尤其是算法、数据结构或系统设计领域,它常常被用来比喻一种在极端约束条件下,实现复杂…

2026/8/11 6:49:17 阅读更多 →
OpenClaw智能体框架在阿里云的高效部署与应用

OpenClaw智能体框架在阿里云的高效部署与应用

1. OpenClaw(Clawdbot)项目概述OpenClaw(又称Clawdbot)是当前AI领域炙手可热的开源智能体框架,它通过模块化设计实现了复杂任务的自动化处理。2026年最新版本在阿里云环境下的部署效率显著提升,5分钟快速搭…

2026/8/11 6:49:17 阅读更多 →
悬剑5渗透测试武器库:5分钟搭建实战环境,快速入门网络安全

悬剑5渗透测试武器库:5分钟搭建实战环境,快速入门网络安全

1. 项目概述:为什么我们需要“悬剑5”这样的武器库如果你刚刚踏入网络安全或者渗透测试这个领域,头几周最让你头疼的事情是什么?我猜,十有八九是“环境配置”。你想复现一个经典的SQL注入漏洞,结果光是在本地搭建一个带…

2026/8/11 6:49:17 阅读更多 →
Flutter自定义路径布局:从CustomMultiChildLayout到贝塞尔曲线实战

Flutter自定义路径布局:从CustomMultiChildLayout到贝塞尔曲线实战

1. 项目概述:当Flutter布局不再“循规蹈矩”在Flutter开发中,我们早已习惯了Row、Column、Stack、Wrap这些布局组件带来的秩序感。它们就像乐高积木的底板,规定了组件只能沿着水平和垂直的轴线,或者通过层叠与换行来排列。这种“网…

2026/8/11 6:49:17 阅读更多 →
打卡 App 为什么越用越焦虑?真正有效的习惯,不该从“连续天数”开始

打卡 App 为什么越用越焦虑?真正有效的习惯,不该从“连续天数”开始

摘要:连续打卡可以带来即时激励,也可能让一次中断变成彻底放弃。本文分析打卡焦虑从何而来,并提供一种不追求全勤、关注生活节奏的温和习惯记录方式。 连续打卡 29 天,第 30 天忘了。 第二天打开 App,看见数字重新回到…

2026/8/11 6:48:16 阅读更多 →

日新闻

如何用Video2X实现专业级视频画质提升:AI视频增强完整指南

如何用Video2X实现专业级视频画质提升:AI视频增强完整指南

如何用Video2X实现专业级视频画质提升:AI视频增强完整指南 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/v…

2026/8/11 0:00:02 阅读更多 →
前后端分离项目中控制台与接口工具数据差异排查指南

前后端分离项目中控制台与接口工具数据差异排查指南

1. 问题现象解析:控制台与Apifox的数据差异 最近在调试一个前后端分离项目时,遇到了一个典型问题:后端服务在本地开发环境控制台能正常输出查询数据,但通过Apifox测试时却返回空结果。这种"控制台有数据,接口工具…

2026/8/11 0:00:03 阅读更多 →
AI编程实战:从Claude Code踩坑到游戏开发入门

AI编程实战:从Claude Code踩坑到游戏开发入门

1. 从“AI能帮我做游戏”到“AI让我重新学编程”最近身边不少朋友,尤其是一些非技术背景、但对游戏开发有浓厚兴趣的朋友,都在问我同一个问题:“听说现在用Claude Code这种AI编程工具,小白也能做游戏了,是真的吗&#…

2026/8/11 0:00:03 阅读更多 →

周新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/11 1:08:05 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/11 1:08:05 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/11 1:08:05 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/10 17:07:33 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/11 1:08:06 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/10 17:07:33 阅读更多 →