Get CPU and GPU Temp Using Python WITHOUT ADMIN ACCESS - Windows

5

I posted this question, asking how to get the CPU and GPU temp on Windows 10: Get CPU and GPU Temp using Python Windows. For that question, I did not include the restriction (at least when I first posted the answer, and for quite a bit after that) for no admin access. I then modified my question to invalidate answers that need admin access (which the only working answer needed). A mod rolled back to a previous version of my question, and asked me to post a new question, so I have done that.

I was wondering if there was a way to get the CPU and the GPU temperature in python. I have already found a way for Linux (using psutil.sensors_temperature), and I wanted to find a way for Windows.

Info:
OS: Windows 10
Python: Python 3.8.3 64-bit (So no 32 bit DLLs)

Below are some of the stuff I tried:

When I try doing the below, I get None (from here - https://stackoverflow.com/a/3264262/13710015):

import wmi
w = wmi.WMI()
prin(w.Win32_TemperatureProbe()[0].CurrentReading)

When I try doing the below, I get an error (from here - https://stackoverflow.com/a/3264262/13710015):

import wmi
w = wmi.WMI(namespace="root\wmi")
temperature_info = w.MSAcpi_ThermalZoneTemperature()[0]
print(temperature_info.CurrentTemperature)

Error:

wmi.x_wmi: <x_wmi: Unexpected COM Error (-2147217396, 'OLE error 0x8004100c', None, None)>

When I tried doing the below, I got (from here - https://stackoverflow.com/a/58924992/13710015):

import ctypes
import ctypes.wintypes as wintypes
from ctypes import windll


LPDWORD = ctypes.POINTER(wintypes.DWORD)
LPOVERLAPPED = wintypes.LPVOID
LPSECURITY_ATTRIBUTES = wintypes.LPVOID

GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000

FILE_SHARE_WRITE=0x00000004
ZERO=0x00000000

CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5

FILE_ATTRIBUTE_NORMAL = 0x00000080

INVALID_HANDLE_VALUE = -1
FILE_DEVICE_UNKNOWN=0x00000022
METHOD_BUFFERED=0
FUNC=0x900
FILE_WRITE_ACCESS=0x002

NULL = 0
FALSE = wintypes.BOOL(0)
TRUE = wintypes.BOOL(1)


def CTL_CODE(DeviceType, Function, Method, Access): return (DeviceType << 16) | (Access << 14) | (Function <<2) | Method




def _CreateFile(filename, access, mode, creation, flags):
    """See: CreateFile function http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).asp """
    CreateFile_Fn = windll.kernel32.CreateFileW
    CreateFile_Fn.argtypes = [
            wintypes.LPWSTR,                    # _In_          LPCTSTR lpFileName
            wintypes.DWORD,                     # _In_          DWORD dwDesiredAccess
            wintypes.DWORD,                     # _In_          DWORD dwShareMode
            LPSECURITY_ATTRIBUTES,              # _In_opt_      LPSECURITY_ATTRIBUTES lpSecurityAttributes
            wintypes.DWORD,                     # _In_          DWORD dwCreationDisposition
            wintypes.DWORD,                     # _In_          DWORD dwFlagsAndAttributes
            wintypes.HANDLE]                    # _In_opt_      HANDLE hTemplateFile
    CreateFile_Fn.restype = wintypes.HANDLE

    return wintypes.HANDLE(CreateFile_Fn(filename,
                         access,
                         mode,
                         NULL,
                         creation,
                         flags,
                         NULL))


handle=_CreateFile('\\\\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,OPEN_EXISTING,ZERO)

def _DeviceIoControl(devhandle, ioctl, inbuf, inbufsiz, outbuf, outbufsiz):
    """See: DeviceIoControl function
http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
"""
    DeviceIoControl_Fn = windll.kernel32.DeviceIoControl
    DeviceIoControl_Fn.argtypes = [
            wintypes.HANDLE,                    # _In_          HANDLE hDevice
            wintypes.DWORD,                     # _In_          DWORD dwIoControlCode
            wintypes.LPVOID,                    # _In_opt_      LPVOID lpInBuffer
            wintypes.DWORD,                     # _In_          DWORD nInBufferSize
            wintypes.LPVOID,                    # _Out_opt_     LPVOID lpOutBuffer
            wintypes.DWORD,                     # _In_          DWORD nOutBufferSize
            LPDWORD,                            # _Out_opt_     LPDWORD lpBytesReturned
            LPOVERLAPPED]                       # _Inout_opt_   LPOVERLAPPED lpOverlapped
    DeviceIoControl_Fn.restype = wintypes.BOOL

    # allocate a DWORD, and take its reference
    dwBytesReturned = wintypes.DWORD(0)
    lpBytesReturned = ctypes.byref(dwBytesReturned)

    status = DeviceIoControl_Fn(devhandle,
                  ioctl,
                  inbuf,
                  inbufsiz,
                  outbuf,
                  outbufsiz,
                  lpBytesReturned,
                  NULL)

    return status, dwBytesReturned

class OUTPUT_temp(ctypes.Structure):
        """See: http://msdn.microsoft.com/en-us/library/aa363972(v=vs.85).aspx"""
        _fields_ = [
                ('Board Temp', wintypes.DWORD),
                ('CPU Temp', wintypes.DWORD),
                ('Board Temp2', wintypes.DWORD),
                ('temp4', wintypes.DWORD),
                ('temp5', wintypes.DWORD)
                ]

class OUTPUT_volt(ctypes.Structure):
        """See: http://msdn.microsoft.com/en-us/library/aa363972(v=vs.85).aspx"""
        _fields_ = [
                ('VCore', wintypes.DWORD),
                ('V(in2)', wintypes.DWORD),
                ('3.3V', wintypes.DWORD),
                ('5.0V', wintypes.DWORD),
                ('temp5', wintypes.DWORD)
                ]

def get_temperature():
    FUNC=0x900
    outDict={}

    ioclt=CTL_CODE(FILE_DEVICE_UNKNOWN, FUNC, METHOD_BUFFERED, FILE_WRITE_ACCESS)

    handle=_CreateFile('\\\\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,OPEN_EXISTING,ZERO)

    win_list = OUTPUT_temp()
    p_win_list = ctypes.pointer(win_list)
    SIZE=ctypes.sizeof(OUTPUT_temp)


    status, output = _DeviceIoControl(handle, ioclt , NULL, ZERO, p_win_list, SIZE)


    for field, typ in win_list._fields_:
                #print ('%s=%d' % (field, getattr(disk_geometry, field)))
                outDict[field]=getattr(win_list,field)
    return outDict

def get_voltages():
    FUNC=0x901
    outDict={}

    ioclt=CTL_CODE(FILE_DEVICE_UNKNOWN, FUNC, METHOD_BUFFERED, FILE_WRITE_ACCESS)

    handle=_CreateFile('\\\\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,OPEN_EXISTING,ZERO)

    win_list = OUTPUT_volt()
    p_win_list = ctypes.pointer(win_list)
    SIZE=ctypes.sizeof(OUTPUT_volt)


    status, output = _DeviceIoControl(handle, ioclt , NULL, ZERO, p_win_list, SIZE)


    for field, typ in win_list._fields_:
                #print ('%s=%d' % (field, getattr(disk_geometry, field)))
                outDict[field]=getattr(win_list,field)
    return outDict

print(OUTPUT_temp._fields_)

Output:

[('Board Temp', <class 'ctypes.c_ulong'>), ('CPU Temp', <class 'ctypes.c_ulong'>), ('Board Temp2', <class 'ctypes.c_ulong'>), ('temp4', <class 'ctypes.c_ulong'>), ('temp5', <class 'ctypes.c_ulong'>)]

I tried this code, and it worked, but it needs admin (from here - https://stackoverflow.com/a/62936850/13710015):

import clr # the pythonnet module.
clr.AddReference(r'YourdllPath')
from OpenHardwareMonitor.Hardware import Computer

c = Computer()
c.CPUEnabled = True # get the Info about CPU
c.GPUEnabled = True # get the Info about GPU
c.Open()
while True:
    for a in range(0, len(c.Hardware[0].Sensors)):
        # print(c.Hardware[0].Sensors[a].Identifier)
        if "/intelcpu/0/temperature" in str(c.Hardware[0].Sensors[a].Identifier):
            print(c.Hardware[0].Sensors[a].get_Value())
            c.Hardware[0].Update()

I tried this code, but it also needed admin (from here - https://stackoverflow.com/a/49909330/13710015):

import clr #package pythonnet, not clr


openhardwaremonitor_hwtypes = ['Mainboard','SuperIO','CPU','RAM','GpuNvidia','GpuAti','TBalancer','Heatmaster','HDD']
cputhermometer_hwtypes = ['Mainboard','SuperIO','CPU','GpuNvidia','GpuAti','TBalancer','Heatmaster','HDD']
openhardwaremonitor_sensortypes = ['Voltage','Clock','Temperature','Load','Fan','Flow','Control','Level','Factor','Power','Data','SmallData']
cputhermometer_sensortypes = ['Voltage','Clock','Temperature','Load','Fan','Flow','Control','Level']


def initialize_openhardwaremonitor():
    file = 'OpenHardwareMonitorLib.dll'
    clr.AddReference(file)

    from OpenHardwareMonitor import Hardware

    handle = Hardware.Computer()
    handle.MainboardEnabled = True
    handle.CPUEnabled = True
    handle.RAMEnabled = True
    handle.GPUEnabled = True
    handle.HDDEnabled = True
    handle.Open()
    return handle

def initialize_cputhermometer():
    file = 'CPUThermometerLib.dll'
    clr.AddReference(file)

    from CPUThermometer import Hardware
    handle = Hardware.Computer()
    handle.CPUEnabled = True
    handle.Open()
    return handle

def fetch_stats(handle):
    for i in handle.Hardware:
        i.Update()
        for sensor in i.Sensors:
            parse_sensor(sensor)
        for j in i.SubHardware:
            j.Update()
            for subsensor in j.Sensors:
                parse_sensor(subsensor)


def parse_sensor(sensor):
        if sensor.Value is not None:
            if type(sensor).__module__ == 'CPUThermometer.Hardware':
                sensortypes = cputhermometer_sensortypes
                hardwaretypes = cputhermometer_hwtypes
            elif type(sensor).__module__ == 'OpenHardwareMonitor.Hardware':
                sensortypes = openhardwaremonitor_sensortypes
                hardwaretypes = openhardwaremonitor_hwtypes
            else:
                return

            if sensor.SensorType == sensortypes.index('Temperature'):
                print(u"%s %s Temperature Sensor #%i %s - %s\u00B0C" % (hardwaretypes[sensor.Hardware.HardwareType], sensor.Hardware.Name, sensor.Index, sensor.Name, sensor.Value))

if __name__ == "__main__":
    print("OpenHardwareMonitor:")
    HardwareHandle = initialize_openhardwaremonitor()
    fetch_stats(HardwareHandle)
    print("\nCPUMonitor:")
    CPUHandle = initialize_cputhermometer()
    fetch_stats(CPUHandle)

I am also fine with using C/C++ extensions with Python, portable command-line apps (which will be run with subprocess.Popen), DLLs (not preferred), and commands (which will be run with subprocess.Popen).

Non-portable apps are not allowed.

python
python-3.x
windows
windows-10
temperature
asked on Stack Overflow Nov 24, 2020 by KetZoomer • edited Dec 7, 2020 by KetZoomer

2 Answers

2

Problem

An unprivileged user needs access to functionality only available by a privileged user in a secure manner.

Solution

Create an server-client interface where functionality is decoupled from the actual system as to prevent security issues (ie: don't just pipe commands or options directly from client for execution by the server).

Consider using gRPC for this server-client interface. If you haven't used gRPC before, here's an example of what this entails:

Create a temperature.proto:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "temperature";
option java_outer_classname = "TemperatureProto";
option objc_class_prefix = "TEMP";

package temperature;

service SystemTemperature {
  rpc GetTemperature (TemperatureRequest) returns (TemperatureReply) {}
}

message TemperatureRequest {
  string name = 1;
}

message TemperatureReply {
  string message = 1;
}

Compile the aforementioned with protoc from protobuf library.

python -m grpc_tools.protoc --proto_path=. temperature.proto --python_out=. --grpc_python_out=.

This will generate a file named temperature_pb2_grpc.py, which is where you'll define functionality and response for GetTemperature, note, that you can implement logic branches contextual upon TemperatureRequest options passed from the client.

Once complete simply write and run a temperature_server.py from your privileged user, and temperature_client.py from your unprivileged user.

References

gRPC: https://grpc.io

gRPC QuickStart guide: https://grpc.io/docs/languages/ruby/quickstart/

protobuf: https://developers.google.com/protocol-buffers/

answered on Stack Overflow Dec 4, 2020 by pygeek • edited Dec 4, 2020 by pygeek
1

This modifies the registry, use at your own risk.

This works with python:

  • step1: Turn off the antivirus protection (I don't know how to do that by automation)
  • step2: Download this repository - https://github.com/YashMakan/get_cpu_gpu_details
  • step3: Extract the files
  • step4: Open app.py file
  • step5: change the variable "file" with the complete path of therm.py, example - C:\\...\\therm.py
  • step6: Run app.py
  • step7: You will get the details
answered on Stack Overflow Dec 4, 2020 by Yash Makan • edited Feb 16, 2021 by KetZoomer

User contributions licensed under CC BY-SA 3.0