Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c5c62a0
WIP: WMI wrapper DLL
Mahasvan Aug 1, 2026
cccaa78
WIP: rename win_new to win, and win to win_old
Mahasvan Aug 1, 2026
7243b8a
Update wmi binding
Mahasvan Aug 1, 2026
9780709
Update wmi.cpp
Mahasvan Aug 1, 2026
d081f14
build: update Windows native binding [skip ci]
github-actions[bot] Aug 1, 2026
39354f5
docs: add reference in docstring for win cpu
Mahasvan Aug 1, 2026
e128bec
Use WMI for Windows CPU data
Mahasvan Aug 1, 2026
e2c97d6
wip: add gpu info bindings
Mahasvan Aug 1, 2026
ec3e2d9
wip: working GPU detection on Windows
Mahasvan Aug 1, 2026
cdeabf0
Update gpu_info.cpp
Mahasvan Aug 1, 2026
7a0e15e
Merge branch 'main' of https://github.com/Mahasvan/HWProbe into feat/…
Mahasvan Aug 1, 2026
3d44843
Delete device_info.dll
Mahasvan Aug 1, 2026
16d175f
build: update Windows native bindings [skip ci]
github-actions[bot] Aug 1, 2026
43f33eb
feat: rewrite windows memory discovery
Mahasvan Aug 1, 2026
9e7bcf6
fix: refactor windows memory discovery, make MemoryModuleSlot fields
Mahasvan Aug 1, 2026
1001eb7
Use new WMI for Windows Storage
Mahasvan Aug 1, 2026
5727a44
feat: rewrite windows network retrieval with new wmi wrapper
Mahasvan Aug 1, 2026
cfe5444
feat: Add display info bindings for windows, revamp windows display
Mahasvan Aug 1, 2026
93f45a1
build: update Windows native bindings [skip ci]
github-actions[bot] Aug 1, 2026
c856be4
fix: build correct acpi path in win display; potentially fix missing
Mahasvan Aug 1, 2026
6f9cc0f
Merge branch 'feat/win-bindings-new' of https://github.com/Mahasvan/H…
Mahasvan Aug 1, 2026
33f97cc
wip: fix monitor name retrieval on windows
Mahasvan Aug 1, 2026
c54e961
build: update Windows native bindings [skip ci]
github-actions[bot] Aug 1, 2026
2a76c5d
improve display detection by combining wmi, dxgi, and setupapi
Mahasvan Aug 1, 2026
9e3123d
Merge branch 'feat/win-bindings-new' of https://github.com/Mahasvan/H…
Mahasvan Aug 1, 2026
646a843
Update display_info.cpp
Mahasvan Aug 1, 2026
ecd17bb
build: update Windows native bindings [skip ci]
github-actions[bot] Aug 1, 2026
79c5006
Revert "build: update Windows native bindings [skip ci]"
Mahasvan Aug 1, 2026
2de52c8
fix: harden VariantToUtf8Slot against null dst and redundant termination
Mahasvan Aug 2, 2026
01d458a
Fix WideCharToMultiByte logic
Mahasvan Aug 2, 2026
cd51ab6
handle edge case in MultiByteToWideChar
Mahasvan Aug 2, 2026
80751b1
fix: handle WideCharToMultiByte failures in gpu_info.cpp
Mahasvan Aug 2, 2026
b5005b6
more descriptive default manufacturer namein windows gpu
Mahasvan Aug 2, 2026
518f179
misc: remove magic numbers from windows cpu instruction check; update
Mahasvan Aug 2, 2026
1756b6c
misc: align dashes in code comments
Mahasvan Aug 2, 2026
2caee82
fix: Improve virtual network adapter detection; update tests
Mahasvan Aug 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies = [
where = ["src"]

[tool.setuptools.package-data]
"hwprobe.interops.win_old.bindings" = ["*.dll"]
"hwprobe.interops.win.bindings" = ["*.dll"]
"hwprobe.interops.mac.bindings" = ["*.dylib"]

Expand Down
138 changes: 26 additions & 112 deletions src/hwprobe/core/windows/cpu.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import ctypes
import os
import winreg
from ctypes import wintypes

from hwprobe.core.windows.win_enum import FEATURE_ID_MAP
from hwprobe.core.windows.win_enum import CPU_ARCHITECTURES, FEATURE_ID_MAP
from hwprobe.models.cpu_models import CPUInfo
from hwprobe.models.status_models import StatusType
from hwprobe.interops.win.bindings import wmi

kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
kernel32.IsProcessorFeaturePresent.argtypes = [wintypes.DWORD]
kernel32.IsProcessorFeaturePresent.restype = wintypes.BOOL

# ARM processor feature IDs for IsProcessorFeaturePresent.
# These are not defined in the Windows SDK headers — they're undocumented
# feature IDs discovered via testing on ARM hardware.
# ref: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
PF_SSVE_FP8DOT2 = 78 # ARMv9 — FEAT_SSVE_FP8DOT2
PF_SSVE_FP8DOT4 = 79 # ARMv9 — FEAT_SSVE_FP8DOT4
PF_SSVE_FP8FMA = 80 # ARMv9 — FEAT_SSVE_FP8FMA
PF_SME_FA64 = 88 # ARMv8 — FEAT_SME_FA64


def is_processor_feature_present(feature_id: int) -> bool:
"""
Expand All @@ -34,10 +42,12 @@ def get_arm_version() -> str:

Otherwise
- we can assume it's ARMv7 or lower.

ref: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
"""
if any([is_processor_feature_present(i) for i in [78, 79, 80]]):
if any(is_processor_feature_present(i) for i in [PF_SSVE_FP8DOT2, PF_SSVE_FP8DOT4, PF_SSVE_FP8FMA]):
return "9"
elif is_processor_feature_present(88):
elif is_processor_feature_present(PF_SME_FA64):
return "8"
else:
return "7 or lower"
Expand All @@ -60,119 +70,23 @@ def get_features() -> list[str]:
return [k for k, v in FEATURE_ID_MAP.items() if is_processor_feature_present(v)]


def parse_registry():
"""
We can get the CPU model name and vendor from the Windows Registry from the following path:
"HKEY_LOCAL_MACHINE -> HARDWARE -> DESCRIPTION -> System -> CentralProcessor -> 0"
"""
key_path = r"HARDWARE\DESCRIPTION\System\CentralProcessor\0"
model_key = "ProcessorNameString"
vendor_key = "VendorIdentifier"

with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path, 0, winreg.KEY_READ) as key:
model_name, _ = winreg.QueryValueEx(key, model_key)
vendor, _ = winreg.QueryValueEx(key, vendor_key)
return model_name, vendor


def get_core_count() -> int:
"""
Uses the GetLogicalProcessorInformation function in the Win32 API to get the number of physical cores.
https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation
"""

"""
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
ULONG_PTR ProcessorMask;
LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
union {
struct {
BYTE Flags;
} ProcessorCore;
struct {
DWORD NodeNumber;
} NumaNode;
CACHE_DESCRIPTOR Cache;
ULONGLONG Reserved[2];
};
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION;

ProcessorMask - Pointer - 8 bytes
Relationship - DWORD - 4 bytes
Padding - 4 byt
Union - must be large enough to hold the largest member - 16 bytes
Total size = 8 + 4 + 4 + 16 = 32 bytes
"""

class SYSTEM_LOGICAL_PROCESSOR_INFORMATION(ctypes.Structure):
_fields_ = [
("ProcessorMask", ctypes.c_size_t),
("Relationship", ctypes.c_int),
("Reserved", ctypes.c_byte * 20),
]

RelationProcessorCore = 0

buffer_size = ctypes.c_ulong(0)
ctypes.windll.kernel32.GetLogicalProcessorInformation(None, ctypes.byref(buffer_size))

count = buffer_size.value // ctypes.sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)
buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION * count)()

ctypes.windll.kernel32.GetLogicalProcessorInformation(buffer, ctypes.byref(buffer_size))

physical_cores = sum(1 for info in buffer if info.Relationship == RelationProcessorCore)

return physical_cores


def fetch_cpu_info() -> CPUInfo:
cpu_info = CPUInfo()
try:
model_name, vendor = parse_registry()
cpu_info.name = model_name.strip()
cpu_info.vendor = "AMD" if "amd" in vendor.lower() else "Intel" if "intel" in vendor.lower() else vendor.strip()

wmi_data = wmi.get_wmi_data("Win32_Processor", ["Name", "Manufacturer", "Architecture", "AddressWidth", "MaxClockSpeed", "NumberOfCores", "NumberOfLogicalProcessors"])
if wmi_data:
cpu_info.name = wmi_data[0]["Name"].strip()
cpu_info.vendor = "AMD" if "amd" in wmi_data[0]["Manufacturer"].lower() else "Intel" if "intel" in wmi_data[0]["Manufacturer"].lower() else wmi_data[0]["Manufacturer"].strip()
cpu_info.architecture = CPU_ARCHITECTURES.get(int(wmi_data[0]["Architecture"]), "Unknown")
cpu_info.bitness = int(wmi_data[0]["AddressWidth"])
cpu_info.cores = int(wmi_data[0]["NumberOfCores"])
cpu_info.threads = int(wmi_data[0]["NumberOfLogicalProcessors"])

features = get_features()
cpu_info.sse_flags = features
except Exception as e:
cpu_info.status.type = StatusType.FAILED
cpu_info.status.messages.append(f"Unable to obtain CPU Info: {e}")
return cpu_info

"""
The CPU Architecture is exposed as an environment variable on Windows systems.
https://www.tenforums.com/tutorials/176966-how-check-if-processor-32-bit-64-bit-arm-windows-10-a.html

Possible outputs:
- x86 -> x86 32-bit
- AMD64 -> x86 64-bit
- ARM64 -> ARM 64-bit

We account for "x86_64" and "i386" as well, just in case.
"""
architecture = os.environ.get("PROCESSOR_ARCHITECTURE", "").lower()
if "amd64" in architecture or "x86_64" in architecture:
cpu_info.architecture = "x86"
cpu_info.bitness = 64
elif "x86" in architecture or "i386" in architecture:
cpu_info.architecture = "x86"
cpu_info.bitness = 32
elif "arm64" in architecture:
cpu_info.architecture = "ARM"
cpu_info.bitness = 64
else:
cpu_info.status.type = StatusType.PARTIAL
cpu_info.status.messages.append("Unknown architecture: " + architecture)

cpu_info.cores = get_core_count()
if not cpu_info.cores:
cpu_info.status.type = StatusType.PARTIAL
cpu_info.status.messages.append(f"Unable to fetch Core Count: {cpu_info.cores}")

cpu_info.threads = os.cpu_count()
if not cpu_info.threads:
cpu_info.status.type = StatusType.PARTIAL
cpu_info.status.messages.append(f"Unable to fetch Threads: {cpu_info.threads}")
cpu_info.status.type = StatusType.FAILED
cpu_info.status.messages.append("Unable to obtain CPU Info: WMI query returned no data.")

return cpu_info
Loading
Loading