Getting CPU and GPU temps with OpenHardwareMonitorLib

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by logan9 » 26 May 2022, 06:26

While searching for a method to get CPU and GPU temps I have found the openhardwaremonitor.lib

I build a Powershell script and with the help of @SKAN RunCMD function I have been able to retrieve the CPU and GPU, temp:

Code: Select all

script =
(
Add-Type -Path "C:\Users\Downloads\OpenHardwareMonitorLib.dll"
$Comp = New-Object -TypeName OpenHardwareMonitor.Hardware.Computer
 
$Comp.Open()
 
$Comp.CPUEnabled           = 1
$Comp.GPUEnabled           = 1 
 
ForEach ($HW in $Comp.Hardware) {
 
   $HW.Update()     
   '# ' + $hw.HardwareType + ' - ' + $hw.name
  
   If ($hw.HardwareType -eq 'CPU') {
      ForEach ($Sensor in $HW.Sensors) {
         #$Sensor.Name + ' - ' + $Sensor.SensorType + ' : ' + $Sensor.value
         If ($Sensor.Name -eq 'CPU Package' -and $Sensor.SensorType -eq [OpenHardwareMonitor.Hardware.SensorType]::Temperature) {
            $Sensor.Name
            $Sensor.value
            Break
        }    
     }         
   }
 
   If ($hw.HardwareType -eq 'GpuNvidia') {
      ForEach ($Sensor in $HW.Sensors) {
         #$Sensor.Name + ' - ' + $Sensor.SensorType + ' : ' + $Sensor.value
         If ($Sensor.SensorType -eq [OpenHardwareMonitor.Hardware.SensorType]::Temperature) {
            $Sensor.Name
            $Sensor.value
            Break            
         }    
     }         
   }
 
   $hw.SubHardware
}

$Comp.Close()
)


psex := "powershell -WindowStyle Hidden " script
MsgBox % RunCMD(psex)
ExitApp


RunCMD(CmdLine, WorkingDir:="", Codepage:="CP0", Fn:="RunCMD_Output") {  ;         RunCMD v0.94        
Local         ; RunCMD v0.94 by SKAN on D34E/D37C @ autohotkey.com/boards/viewtopic.php?t=74647                                                             
Global A_Args ; Based on StdOutToVar.ahk by Sean @ autohotkey.com/board/topic/15455-stdouttovar

  Fn := IsFunc(Fn) ? Func(Fn) : 0
, DllCall("CreatePipe", "PtrP",hPipeR:=0, "PtrP",hPipeW:=0, "Ptr",0, "Int",0)
, DllCall("SetHandleInformation", "Ptr",hPipeW, "Int",1, "Int",1)
, DllCall("SetNamedPipeHandleState","Ptr",hPipeR, "UIntP",PIPE_NOWAIT:=1, "Ptr",0, "Ptr",0)

, P8 := (A_PtrSize=8)
, VarSetCapacity(SI, P8 ? 104 : 68, 0)                          ; STARTUPINFO structure      
, NumPut(P8 ? 104 : 68, SI)                                     ; size of STARTUPINFO
, NumPut(STARTF_USESTDHANDLES:=0x100, SI, P8 ? 60 : 44,"UInt")  ; dwFlags
, NumPut(hPipeW, SI, P8 ? 88 : 60)                              ; hStdOutput
, NumPut(hPipeW, SI, P8 ? 96 : 64)                              ; hStdError
, VarSetCapacity(PI, P8 ? 24 : 16)                              ; PROCESS_INFORMATION structure

  If not DllCall("CreateProcess", "Ptr",0, "Str",CmdLine, "Ptr",0, "Int",0, "Int",True
                ,"Int",0x08000000 | DllCall("GetPriorityClass", "Ptr",-1, "UInt"), "Int",0
                ,"Ptr",WorkingDir ? &WorkingDir : 0, "Ptr",&SI, "Ptr",&PI)  
     Return Format("{1:}", "", ErrorLevel := -1
                   ,DllCall("CloseHandle", "Ptr",hPipeW), DllCall("CloseHandle", "Ptr",hPipeR))

  DllCall("CloseHandle", "Ptr",hPipeW)
, A_Args.RunCMD := { "PID": NumGet(PI, P8? 16 : 8, "UInt") }      
, File := FileOpen(hPipeR, "h", Codepage)

, LineNum := 1,  sOutput := ""
  While (A_Args.RunCMD.PID + DllCall("Sleep", "Int",0))
    and DllCall("PeekNamedPipe", "Ptr",hPipeR, "Ptr",0, "Int",0, "Ptr",0, "Ptr",0, "Ptr",0)
        While A_Args.RunCMD.PID and (Line := File.ReadLine())
          sOutput .= Fn ? Fn.Call(Line, LineNum++) : Line

  A_Args.RunCMD.PID := 0
, hProcess := NumGet(PI, 0)
, hThread  := NumGet(PI, A_PtrSize)

, DllCall("GetExitCodeProcess", "Ptr",hProcess, "PtrP",ExitCode:=0)
, DllCall("CloseHandle", "Ptr",hProcess)
, DllCall("CloseHandle", "Ptr",hThread)
, DllCall("CloseHandle", "Ptr",hPipeR)

, ErrorLevel := ExitCode

Return sOutput  
}

The script above is working correctly, the DLL lib can be compiled with the source on the link above, I also attached the DLL case someone else needs.

I was searching for an alternative method that doesn't make use of PowerShell because this method takes more time to retrieve the data, and found @lexikos .NET Framework CLR lib

But I'm not being able to get it working, looking for any help on this task

Code: Select all

Lib                 := "C:\Users\Downloads\OpenHardwareMonitorLib.dll"
asm                 := CLR_LoadLibrary(Lib)
computer            := CLR_CreateObject(asm, "OpenHardwareMonitor.Hardware.Computer")
computer.Open()
computer.CpuEnabled := 1
Hardwares           := computer.Hardware
msgbox,%  ComObjType(Hardwares[0])  ;return  9
msgbox,%  ComObjType(Hardwares[0].HardwareType)  ; Error:  0x80131509 -  Specifically: HardwareType

Attachments
OpenHardwareMonitorLib.rar
(109.17 KiB) Downloaded 109 times

BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by BoBo » 26 May 2022, 07:31

viewtopic.php?p=230533#p230533 (CPU temperature)
viewtopic.php?f=83&t=94947 (GPU temperature, AHK 2.x)
JFTR.

logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by logan9 » 26 May 2022, 07:42

viewtopic.php?p=230533#p230533 (CPU temperature)
the value returned by this, is not the CPU temperature
viewtopic.php?f=83&t=94947 (GPU temperature, AHK 2.x)
im using v1, also OpenHardwareMonitor can return a lot more info indepent of AMD/NVIDIA and not only CPU/GPU

Example
Spoiler

BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by BoBo » 26 May 2022, 07:58

Interesting, as the scripts first line is saying this: " ;get CPU temperature "
and the author of that post (@jeeswg) is referencing that:…
- This link gives an idea of what the normal and maximum temperatures are in Celsius:
CPU Temperature - What are the Maximum and Normal CPU Temps?
http://www.buildcomputers.net/cpu-temperature.html

- @RonJeanJr. Re. the temperature units:
How can we get a CPU temperature through WMI? - Stack Overflow
https://stackoverflow.com/questions/457 ... hrough-wmi
the results are in Kelvin * 10, so you need to divide the result by 10, and then subtract 273.15 to get °Celsius.
So, (asking myself) what is that post about? :think:

logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by logan9 » 26 May 2022, 08:07

in the link of your previous answer, there's a link to StackOverflow
https://stackoverflow.com/questions/45736193/how-can-we-get-a-cpu-temperature-through-wmi
this is not direct CPU temp but somewhere on the MB: stackoverflow.com/a/17083409/1747983 –
Tilo
Oct 24, 2018 at 20:00
and
To get the exact temperature of CPU (and every core) you need to write kernel drivers, what is much more complicated
https://stackoverflow.com/a/17083409/19065435

also, if you test jeeswg script and compare it with some monitoring tool like HWMonitor, you'll see that the value returned is not the CPU temp
I think the value is the temp of the motherboard

BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by BoBo » 26 May 2022, 15:42

Thx for that correction :thumbup:

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by jNizM » 27 May 2022, 04:55

AutoHotkey can do more informations for Nvidia Cards too:
viewtopic.php?f=83&t=95112 (NVIDIA NvAPI)
viewtopic.php?f=83&t=95175 (NVIDIA NVML)

btw there is a fork called LibreHardwareMonitor because the developer of OHM stopped 2020
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

pv007
Posts: 93
Joined: 20 Jul 2020, 23:50

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by pv007 » 27 May 2022, 05:34

jNizM wrote:
27 May 2022, 04:55
AutoHotkey can do more informations for Nvidia Cards too:
viewtopic.php?f=83&t=95112 (NVIDIA NvAPI)
viewtopic.php?f=83&t=95175 (NVIDIA NVML)
great job! does any of your libs or SysMeter support read CPU temperature, use etc?

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by lexikos » 27 May 2022, 06:21

0x80131509 corresponds to COR_E_INVALIDOPERATION (InvalidOperationException).

I think there is something about the hardware class (OpenHardwareMonitor.Hardware.CPU.IntelCPU in my case) which COM interop cannot handle. Any attempt to invoke the object through COM gives the same result, including .GetType().

You would likely have better luck creating helper objects in C#, or even invoking PowerShell via CLR (but don't ask me how to do that).

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by jNizM » 27 May 2022, 10:43

pv007 wrote:
27 May 2022, 05:34
great job! does any of your libs or SysMeter support read CPU temperature, use etc?
e.g. with viewtopic.php?f=83&t=104451 (PDH - Performance Counters)
But CPU Temeprature must be supported from your system with PDH. Same for MSAcpi_ThermalZoneTemperature (wmi)


But you could also try with https://www.alcpu.com/CoreTemp/developers.html (CoreTemp)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by malcev » 29 May 2022, 15:37

logan9 wrote:
26 May 2022, 06:26
I was searching for an alternative method that doesn't make use of PowerShell because this method takes more time to retrieve the data, and found @lexikos .NET Framework
To invoke PowerShell via CLR You can try like this. (May be need run as admin).

Code: Select all

OpenHardwareMonitorLib := A_ScriptDir "\OpenHardwareMonitorLib.dll"
cSharp =
(
using System.Text;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

public class ps
{
	public string RunScript(string scriptText)
	{
		Runspace runspace = RunspaceFactory.CreateRunspace();
		runspace.Open();

		Pipeline pipeline = runspace.CreatePipeline();
		pipeline.Commands.AddScript(scriptText);
		pipeline.Commands.Add("Out-String");

		Collection<PSObject> results = pipeline.Invoke();

		runspace.Close();
		return results[0].ToString();
		
		/*
		StringBuilder stringBuilder = new StringBuilder();
		foreach (PSObject obj in results)
		{
			stringBuilder.AppendLine(obj.ToString());
		}

		return stringBuilder.ToString();
		*/
	}
}
)

asm := CLR_CompileC#( cSharp, "System.Core.dll | C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll" )
ps := asm.CreateInstance("ps")
psCode =
(
[Reflection.Assembly]::LoadFile("%OpenHardwareMonitorLib%") | Out-Null
$Comp = New-Object -TypeName OpenHardwareMonitor.Hardware.Computer
$Comp.Open()
$Comp.CPUEnabled = $true
$Comp.RAMEnabled = $true
$Comp.MainboardEnabled = $true
$Comp.FanControllerEnabled = $true
$Comp.GPUEnabled = $true
$Comp.HDDEnabled = $true
ForEach ($HW in $Comp.Hardware) {
$HW.Update()
    $hw.HardwareType.ToString() + ' - ' + $hw.name.ToString()
    If ( $hw.HardwareType -eq "CPU"){
        ForEach ($Sensor in $HW.Sensors) {
        If ($Sensor.SensorType -eq "Temperature"){
            $Sensor.Name + ' - Temp : ' + $Sensor.Value.ToString() + ' C - Min. : ' + $Sensor.Min.ToString() + ' C - Max : ' + $Sensor.Max.ToString() + ' C'
        }
      }
    }
    # $hw.Sensors
    $hw.SubHardware
}
$Comp.Close()
)
msgbox % ps.RunScript(psCode)

; ==========================================================
;                  .NET Framework Interop
;      http://www.autohotkey.com/forum/topic26191.html
; ==========================================================
;
;   Author:     Lexikos
;   Version:    1.2
;   Requires:	AutoHotkey_L v1.0.96+
;

CLR_LoadLibrary(AssemblyName, AppDomain=0)
{
	if !AppDomain
		AppDomain := CLR_GetDefaultDomain()
	e := ComObjError(0)
	Loop 1 {
		if assembly := AppDomain.Load_2(AssemblyName)
			break
		static null := ComObject(13,0)
		args := ComObjArray(0xC, 1),  args[0] := AssemblyName
		typeofAssembly := AppDomain.GetType().Assembly.GetType()
		if assembly := typeofAssembly.InvokeMember_3("LoadWithPartialName", 0x158, null, null, args)
			break
		if assembly := typeofAssembly.InvokeMember_3("LoadFrom", 0x158, null, null, args)
			break
	}
	ComObjError(e)
	return assembly
}

CLR_CreateObject(Assembly, TypeName, Args*)
{
	if !(argCount := Args.MaxIndex())
		return Assembly.CreateInstance_2(TypeName, true)
	
	vargs := ComObjArray(0xC, argCount)
	Loop % argCount
		vargs[A_Index-1] := Args[A_Index]
	
	static Array_Empty := ComObjArray(0xC,0), null := ComObject(13,0)
	
	return Assembly.CreateInstance_3(TypeName, true, 0, null, vargs, null, Array_Empty)
}

CLR_CompileC#(Code, References="", AppDomain=0, FileName="", CompilerOptions="")
{
	return CLR_CompileAssembly(Code, References, "System", "Microsoft.CSharp.CSharpCodeProvider", AppDomain, FileName, CompilerOptions)
}

CLR_CompileVB(Code, References="", AppDomain=0, FileName="", CompilerOptions="")
{
	return CLR_CompileAssembly(Code, References, "System", "Microsoft.VisualBasic.VBCodeProvider", AppDomain, FileName, CompilerOptions)
}

CLR_StartDomain(ByRef AppDomain, BaseDirectory="")
{
	static null := ComObject(13,0)
	args := ComObjArray(0xC, 5), args[0] := "", args[2] := BaseDirectory, args[4] := ComObject(0xB,false)
	AppDomain := CLR_GetDefaultDomain().GetType().InvokeMember_3("CreateDomain", 0x158, null, null, args)
	return A_LastError >= 0
}

CLR_StopDomain(ByRef AppDomain)
{	; ICorRuntimeHost::UnloadDomain
	DllCall("SetLastError", "uint", hr := DllCall(NumGet(NumGet(0+RtHst:=CLR_Start())+20*A_PtrSize), "ptr", RtHst, "ptr", ComObjValue(AppDomain))), AppDomain := ""
	return hr >= 0
}

; NOTE: IT IS NOT NECESSARY TO CALL THIS FUNCTION unless you need to load a specific version.
CLR_Start(Version="") ; returns ICorRuntimeHost*
{
	static RtHst := 0
	; The simple method gives no control over versioning, and seems to load .NET v2 even when v4 is present:
	; return RtHst ? RtHst : (RtHst:=COM_CreateObject("CLRMetaData.CorRuntimeHost","{CB2F6722-AB3A-11D2-9C40-00C04FA30A3E}"), DllCall(NumGet(NumGet(RtHst+0)+40),"uint",RtHst))
	if RtHst
		return RtHst
	EnvGet SystemRoot, SystemRoot
	if Version =
		Loop % SystemRoot "\Microsoft.NET\Framework" (A_PtrSize=8?"64":"") "\*", 2
			if (FileExist(A_LoopFileFullPath "\mscorlib.dll") && A_LoopFileName > Version)
				Version := A_LoopFileName
	if DllCall("mscoree\CorBindToRuntimeEx", "wstr", Version, "ptr", 0, "uint", 0
	, "ptr", CLR_GUID(CLSID_CorRuntimeHost, "{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}")
	, "ptr", CLR_GUID(IID_ICorRuntimeHost,  "{CB2F6722-AB3A-11D2-9C40-00C04FA30A3E}")
	, "ptr*", RtHst) >= 0
		DllCall(NumGet(NumGet(RtHst+0)+10*A_PtrSize), "ptr", RtHst) ; Start
	return RtHst
}

;
; INTERNAL FUNCTIONS
;

CLR_GetDefaultDomain()
{
	static defaultDomain := 0
	if !defaultDomain
	{	; ICorRuntimeHost::GetDefaultDomain
		if DllCall(NumGet(NumGet(0+RtHst:=CLR_Start())+13*A_PtrSize), "ptr", RtHst, "ptr*", p:=0) >= 0
			defaultDomain := ComObject(p), ObjRelease(p)
	}
	return defaultDomain
}

CLR_CompileAssembly(Code, References, ProviderAssembly, ProviderType, AppDomain=0, FileName="", CompilerOptions="")
{
	if !AppDomain
		AppDomain := CLR_GetDefaultDomain()
	
	if !(asmProvider := CLR_LoadLibrary(ProviderAssembly, AppDomain))
	|| !(codeProvider := asmProvider.CreateInstance(ProviderType))
	|| !(codeCompiler := codeProvider.CreateCompiler())
		return 0

	if !(asmSystem := (ProviderAssembly="System") ? asmProvider : CLR_LoadLibrary("System", AppDomain))
		return 0
	
	; Convert | delimited list of references into an array.
	StringSplit, Refs, References, |, %A_Space%%A_Tab%
	aRefs := ComObjArray(8, Refs0)
	Loop % Refs0
		aRefs[A_Index-1] := Refs%A_Index%
	
	; Set parameters for compiler.
	prms := CLR_CreateObject(asmSystem, "System.CodeDom.Compiler.CompilerParameters", aRefs)
	, prms.OutputAssembly          := FileName
	, prms.GenerateInMemory        := FileName=""
	, prms.GenerateExecutable      := SubStr(FileName,-3)=".exe"
	, prms.CompilerOptions         := CompilerOptions
	, prms.IncludeDebugInformation := true
	
	; Compile!
	compilerRes := codeCompiler.CompileAssemblyFromSource(prms, Code)
	
	if error_count := (errors := compilerRes.Errors).Count
	{
		error_text := ""
		Loop % error_count
			error_text .= ((e := errors.Item[A_Index-1]).IsWarning ? "Warning " : "Error ") . e.ErrorNumber " on line " e.Line ": " e.ErrorText "`n`n"
		MsgBox, 16, Compilation Failed, %error_text%
		return 0
	}
	; Success. Return Assembly object or path.
	return compilerRes[FileName="" ? "CompiledAssembly" : "PathToAssembly"]
}

CLR_GUID(ByRef GUID, sGUID)
{
	VarSetCapacity(GUID, 16, 0)
	return DllCall("ole32\CLSIDFromString", "wstr", sGUID, "ptr", &GUID) >= 0 ? &GUID : ""
}

logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by logan9 » 01 Jun 2022, 07:25

malcev wrote:
29 May 2022, 15:37
To invoke PowerShell via CLR You can try like this. (May be need run as admin).
Thank you Malcev, your example did work on my side.
What's the difference from this method and the function CMDRet()
I was looking into task manager and I could see it does not launch an instance of powershell.exe?

Do i need to 'release' or 'free' something?
1 min running in a loop with 300sec of sleep, the mem keeps growing
image.png
image.png (4.37 KiB) Viewed 2748 times
Do you have any tips to improve performance when the script Is called multiple times constantly?
When I execute the script directly into PowerShell it takes around 150ms, running using ahk takes 500~600ms.

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by jNizM » 01 Jun 2022, 13:05

This could be a memory leak, where the memory that is no longer used is not released.

Btw. did you looked into the fork LibreHardwareMonitor since OpenHardwareMontitore is no longer developed?
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by malcev » 01 Jun 2022, 18:10

You can free memory with psapi.dll\EmptyWorkingSet.
Also You can try to run powershell script via ActiveXPosh com object.
https://www.autoitscript.com/forum/topic/204729-powershell-com-files/

logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Getting CPU and GPU temps with OpenHardwareMonitorLib

Post by logan9 » 02 Jun 2022, 13:07

Code: Select all

Loop {
x := ps.RunScript(psCode)
x := ""
sleep, 100
}
Return



f2::
PID := DllCall("GetCurrentProcessId")

for objItem in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_Process") {
   try {
      if (objItem.ProcessID = PID) {
            hProcess := DllCall("OpenProcess", "uint", 0x001F0FFF, "int", 0, "uint", objItem.ProcessID, "ptr")
            DllCall("SetProcessWorkingSetSize", "ptr", hProcess, "uptr", -1, "uptr", -1)
            DllCall("psapi.dll\EmptyWorkingSet", "ptr", hProcess)
            DllCall("CloseHandle", "ptr", hProcess)
      }
   }
}
DllCall("psapi.dll\EmptyWorkingSet", "ptr", -1)
return
mem continue the same, and it's a bad idea to call some sort of mem clean function to counter a mem leak

jNizM, yes I do have looked on it, but at the ends, there's the same problem: a way to call it from ahk

Post Reply

Return to “Ask for Help (v1)”