mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
CPU (Windows): use perflib.h instead of pdh.h for querying CPU temp
Fix #1787
This commit is contained in:
@@ -1593,7 +1593,6 @@ elseif(WIN32)
|
||||
PRIVATE "winbrand"
|
||||
PRIVATE "propsys"
|
||||
PRIVATE "secur32"
|
||||
PRIVATE "pdh"
|
||||
)
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
||||
# WoA only works on Windows 10 or higher
|
||||
|
||||
@@ -4,42 +4,116 @@
|
||||
#include "util/mallocHelper.h"
|
||||
#include "util/smbiosHelper.h"
|
||||
|
||||
#include <pdh.h>
|
||||
#include <windows.h>
|
||||
#include "perflib_.h"
|
||||
#include <wchar.h>
|
||||
|
||||
static void ffPdhOpenCloseQuery(HQUERY* query)
|
||||
static inline void ffPerfCloseQueryHandle(HANDLE* phQuery)
|
||||
{
|
||||
assert(query);
|
||||
if (*query)
|
||||
if (*phQuery != NULL)
|
||||
{
|
||||
PdhCloseQuery(*query);
|
||||
*query = NULL;
|
||||
PerfCloseQueryHandle(*phQuery);
|
||||
*phQuery = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* detectThermalTemp(double* result)
|
||||
const char* detectThermalTemp(double* result)
|
||||
{
|
||||
// typeperf.exe -sc 1 "\Thermal Zone Information(*)\Temperature"
|
||||
struct FFPerfQuerySpec
|
||||
{
|
||||
PERF_COUNTER_IDENTIFIER Identifier;
|
||||
WCHAR Name[16];
|
||||
} querySpec = {
|
||||
.Identifier = {
|
||||
// Thermal Zone Information
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{52bc5412-dac2-449c-8bc2-96443888fe6b}
|
||||
.CounterSetGuid = { 0x52bc5412, 0xdac2, 0x449c, {0x8b, 0xc2, 0x96, 0x44, 0x38, 0x88, 0xfe, 0x6b} },
|
||||
.Size = sizeof(querySpec),
|
||||
.CounterId = PERF_WILDCARD_COUNTER,
|
||||
.InstanceId = PERF_WILDCARD_COUNTER,
|
||||
},
|
||||
.Name = L"\\_TZ.CPUZ", // The standard(?) instance name for CPU temperature in the thermal provider
|
||||
};
|
||||
|
||||
__attribute__((__cleanup__(ffPdhOpenCloseQuery))) HQUERY query = NULL;
|
||||
DWORD dataSize = 0;
|
||||
if (PerfEnumerateCounterSetInstances(NULL, &querySpec.Identifier.CounterSetGuid, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
|
||||
return "PerfEnumerateCounterSetInstances() failed";
|
||||
|
||||
if (PdhOpenQueryW(NULL, 0, &query) != ERROR_SUCCESS)
|
||||
return "Failed to open PDH query";
|
||||
if (dataSize <= sizeof(PERF_INSTANCE_HEADER))
|
||||
return "No `Thermal Zone Information` instances found";
|
||||
|
||||
HCOUNTER counter = NULL;
|
||||
if (PdhAddEnglishCounterW(query,
|
||||
L"\\Thermal Zone Information(*)\\Temperature",
|
||||
0,
|
||||
&counter) != ERROR_SUCCESS)
|
||||
return "Failed to add TZI temperature counter";
|
||||
{
|
||||
FF_AUTO_FREE PERF_INSTANCE_HEADER* const pHead = malloc(dataSize);
|
||||
if (PerfEnumerateCounterSetInstances(NULL, &querySpec.Identifier.CounterSetGuid, pHead, dataSize, &dataSize) != ERROR_SUCCESS)
|
||||
return "PerfEnumerateCounterSetInstances() failed to get instance headers";
|
||||
|
||||
if (PdhCollectQueryData(query) != ERROR_SUCCESS)
|
||||
return "Failed to collect query data";
|
||||
PERF_INSTANCE_HEADER* pInstanceHeader = pHead;
|
||||
while (1)
|
||||
{
|
||||
const wchar_t* instanceName = (const wchar_t*)((BYTE*)pInstanceHeader + sizeof(*pInstanceHeader));
|
||||
if (wcscmp(instanceName, querySpec.Name) == 0)
|
||||
break;
|
||||
|
||||
PDH_FMT_COUNTERVALUE value;
|
||||
if (PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, NULL, &value) != ERROR_SUCCESS)
|
||||
return "Failed to format counter value";
|
||||
dataSize -= pInstanceHeader->Size;
|
||||
if (dataSize == 0)
|
||||
break;
|
||||
pInstanceHeader = (PERF_INSTANCE_HEADER*)((BYTE*)pInstanceHeader + pInstanceHeader->Size);
|
||||
}
|
||||
|
||||
*result = value.doubleValue - 273;
|
||||
if (dataSize == 0)
|
||||
{
|
||||
const wchar_t* instanceName = (const wchar_t*)((BYTE*)pHead + sizeof(*pHead));
|
||||
wcscpy(querySpec.Name, instanceName); // Use the first instance name if the specific one is not found
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((__cleanup__(ffPerfCloseQueryHandle)))
|
||||
HANDLE hQuery = NULL;
|
||||
|
||||
if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS)
|
||||
return "PerfOpenQueryHandle() failed";
|
||||
|
||||
if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS)
|
||||
return "PerfAddCounters() failed";
|
||||
|
||||
if (querySpec.Identifier.Status != ERROR_SUCCESS)
|
||||
return "PerfAddCounters() reports invalid identifier";
|
||||
|
||||
if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
|
||||
return "PerfQueryCounterData(NULL) failed";
|
||||
|
||||
if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER)) // PERF_ERROR_RETURN, should not happen
|
||||
return "instance doesn't exist";
|
||||
|
||||
FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = malloc(dataSize);
|
||||
|
||||
if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS)
|
||||
return "PerfQueryCounterData(pDataHeader) failed";
|
||||
|
||||
PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*)(pDataHeader + 1);
|
||||
if (pCounterHeader->dwType != PERF_MULTIPLE_COUNTERS)
|
||||
return "Invalid counter type";
|
||||
|
||||
PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*)(pCounterHeader + 1);
|
||||
PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pMultiCounters + pMultiCounters->dwSize);
|
||||
|
||||
for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++)
|
||||
{
|
||||
if (pCounterData->dwDataSize == sizeof(int32_t))
|
||||
{
|
||||
DWORD* pCounterIds = (DWORD*)(pMultiCounters + 1);
|
||||
switch (pCounterIds[iCounter]) {
|
||||
case 0: // Temperature
|
||||
*result = *(int32_t*)(pCounterData + 1) - 273;
|
||||
break;
|
||||
case 3: // High Precision Temperature
|
||||
*result = *(int32_t*)(pCounterData + 1) / 10.0 - 273;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pCounterData + pCounterData->dwSize);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <perflib.h>
|
||||
|
||||
// Missing from <perflib.h> of MinGW-w64 SDK
|
||||
|
||||
#define PERF_WILDCARD_COUNTER 0xFFFFFFFF
|
||||
#define PERF_WILDCARD_INSTANCE L"*"
|
||||
#define PERF_AGGREGATE_INSTANCE L"_Total"
|
||||
#define PERF_MAX_INSTANCE_NAME 1024
|
||||
|
||||
typedef struct _PERF_INSTANCE_HEADER {
|
||||
ULONG Size; // = sizeof(PERF_INSTANCE_HEADER) + sizeof(InstanceName) + sizeof(Padding)
|
||||
ULONG InstanceId; // Instance ID.
|
||||
// Followed by:
|
||||
// WCHAR InstanceName[]; // Nul-terminated.
|
||||
// WCHAR Padding[]; // Pad to a multiple of 8 bytes
|
||||
} PERF_INSTANCE_HEADER, *PPERF_INSTANCE_HEADER;
|
||||
|
||||
typedef struct _PERF_COUNTER_IDENTIFIER {
|
||||
GUID CounterSetGuid; // The GUID of the counterset.
|
||||
ULONG Status; // Win32 error code indicating success/failure of the add/delete operation.
|
||||
ULONG Size; // sizeof(PERF_COUNTER_IDENTIFIER) + sizeof(InstanceName) + sizeof(Padding)
|
||||
ULONG CounterId; // CounterId, or PERF_WILDCARD_COUNTER for all counters.
|
||||
ULONG InstanceId; // InstanceId, or 0xFFFFFFFF to not filter on instance ID.
|
||||
ULONG Index; // Set by PerfQueryCounterInfo to the position in which the corresponding counter data is returned.
|
||||
ULONG Reserved; // Reserved.
|
||||
// Followed by:
|
||||
// WCHAR InstanceName[];
|
||||
// WCHAR Padding[];
|
||||
} PERF_COUNTER_IDENTIFIER, * PPERF_COUNTER_IDENTIFIER;
|
||||
|
||||
typedef struct _PERF_DATA_HEADER {
|
||||
ULONG dwTotalSize; // = sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER blocks...)
|
||||
ULONG dwNumCounters; // The number of PERF_COUNTER_HEADER blocks.
|
||||
LONGLONG PerfTimeStamp; // Timestamp from a high-resolution clock.
|
||||
LONGLONG PerfTime100NSec; // The number of 100 nanosecond intervals since January 1, 1601, in Coordinated Universal Time (UTC).
|
||||
LONGLONG PerfFreq; // The frequency of a high-resolution clock.
|
||||
SYSTEMTIME SystemTime; // The time at which data is collected on the provider side.
|
||||
// Followed by:
|
||||
// PERF_COUNTER_HEADER blocks...;
|
||||
} PERF_DATA_HEADER, * PPERF_DATA_HEADER;
|
||||
|
||||
typedef enum _PerfCounterDataType {
|
||||
PERF_ERROR_RETURN = 0, /* An error occurred when the performance counter value was queried. */
|
||||
PERF_SINGLE_COUNTER = 1, /* Query returned a single counter from a single-instance. */
|
||||
PERF_MULTIPLE_COUNTERS = 2, /* Query returned multiple counters from a single instance. */
|
||||
PERF_MULTIPLE_INSTANCES = 4, /* Query returned a single counter from each of multiple instances. */
|
||||
PERF_COUNTERSET = 6 /* Query returned multiple counters from each of multiple instances. */
|
||||
} PerfCounterDataType;
|
||||
|
||||
typedef struct _PERF_COUNTER_HEADER {
|
||||
ULONG dwStatus; // Win32 error code indicating success/failure of the query operation.
|
||||
PerfCounterDataType dwType; // Result type - error, single/single, multi/single, single/multi, multi/multi.
|
||||
ULONG dwSize; // = sizeof(PERF_COUNTER_HEADER) + sizeof(Additional data)
|
||||
ULONG Reserved; // Reserved.
|
||||
// Followed by additional data:
|
||||
// If dwType == PERF_ERROR_RETURN: nothing.
|
||||
// If dwType == PERF_SINGLE_COUNTER: PERF_COUNTER_DATA block.
|
||||
// If dwType == PERF_MULTIPLE_COUNTERS: PERF_MULTI_COUNTERS block + PERF_COUNTER_DATA blocks.
|
||||
// If dwType == PERF_MULTIPLE_INSTANCES: PERF_MULTI_INSTANCES block.
|
||||
// If dwType == PERF_COUNTERSET: PERF_MULTI_COUNTERS block + PERF_MULTI_INSTANCES block.
|
||||
} PERF_COUNTER_HEADER, * PPERF_COUNTER_HEADER;
|
||||
|
||||
typedef struct _PERF_MULTI_COUNTERS {
|
||||
ULONG dwSize; // sizeof(PERF_MULTI_COUNTERS) + sizeof(CounterIds)
|
||||
ULONG dwCounters; // Number of counter ids.
|
||||
// Followed by:
|
||||
// DWORD CounterIds[dwCounters];
|
||||
} PERF_MULTI_COUNTERS, * PPERF_MULTI_COUNTERS;
|
||||
|
||||
typedef struct _PERF_COUNTER_DATA {
|
||||
ULONG dwDataSize; // Size of the counter data, in bytes.
|
||||
ULONG dwSize; // = sizeof(PERF_COUNTER_DATA) + sizeof(Data) + sizeof(Padding)
|
||||
// Followed by:
|
||||
// BYTE Data[dwDataSize];
|
||||
// BYTE Padding[];
|
||||
} PERF_COUNTER_DATA, * PPERF_COUNTER_DATA;
|
||||
|
||||
_Success_(return == ERROR_SUCCESS)
|
||||
ULONG
|
||||
WINAPI
|
||||
PerfEnumerateCounterSetInstances(
|
||||
_In_opt_z_ LPCWSTR szMachine,
|
||||
_In_ LPCGUID pCounterSetId,
|
||||
_Out_opt_bytecap_post_bytecount_(cbInstances, *pcbInstancesActual) PPERF_INSTANCE_HEADER pInstances,
|
||||
DWORD cbInstances,
|
||||
_Out_ LPDWORD pcbInstancesActual
|
||||
);
|
||||
|
||||
_Success_(return == ERROR_SUCCESS)
|
||||
ULONG
|
||||
WINAPI
|
||||
PerfOpenQueryHandle(
|
||||
_In_opt_z_ LPCWSTR szMachine,
|
||||
_Out_ HANDLE * phQuery
|
||||
);
|
||||
|
||||
_Success_(return == ERROR_SUCCESS)
|
||||
ULONG
|
||||
WINAPI
|
||||
PerfCloseQueryHandle(
|
||||
_In_ HANDLE hQuery
|
||||
);
|
||||
|
||||
_Success_(return == ERROR_SUCCESS)
|
||||
ULONG
|
||||
WINAPI
|
||||
PerfAddCounters(
|
||||
_In_ HANDLE hQuery,
|
||||
_Inout_bytecount_(cbCounters) PPERF_COUNTER_IDENTIFIER pCounters,
|
||||
DWORD cbCounters
|
||||
);
|
||||
|
||||
_Success_(return == ERROR_SUCCESS)
|
||||
ULONG
|
||||
WINAPI
|
||||
PerfDeleteCounters(
|
||||
_In_ HANDLE hQuery,
|
||||
_Inout_bytecount_(cbCounters) PPERF_COUNTER_IDENTIFIER pCounters,
|
||||
DWORD cbCounters
|
||||
);
|
||||
|
||||
_Success_(return == ERROR_SUCCESS)
|
||||
ULONG
|
||||
WINAPI
|
||||
PerfQueryCounterData(
|
||||
_In_ HANDLE hQuery,
|
||||
_Out_opt_bytecap_post_bytecount_(cbCounterBlock, *pcbCounterBlockActual) PPERF_DATA_HEADER pCounterBlock,
|
||||
DWORD cbCounterBlock,
|
||||
_Out_ LPDWORD pcbCounterBlockActual
|
||||
);
|
||||
Reference in New Issue
Block a user