How can we get a CPU temperature through WMI? - wmi

I installed WMI code creator from here, and I'm wondering how we can use it to get the CPU temperature.
The application gives many options (as shown below), but I am not sure where I have to click to get the CPU temperature.
I went to the description of WMI code creator and saw the following:
The WMI Code Creator tool allows you to generate VBScript, C#, and VB
.NET code that uses WMI to complete a management task such as querying
for management data, executing a method from a WMI class, or receiving
event notifications using WMI.

Namespace: root\wmi
Path: MSAcpi_ThermalZoneTemperature
To run this (using wmic) from the Windows command line (cmd.exe) the command would be:
wmic /namespace:\\root\wmi PATH MSAcpi_ThermalZoneTemperature get CriticalTripPoint, CurrentTemperature
Attention: the results are in Kelvin * 10, so you need to divide the result by 10, and then subtract 273.15 to get °Celsius.
More information:
WUtils.com : MSAcpi_ThermalZoneTemperature Properties
Wikipedia : Kelvin (and conversion to/from °C and °F)
Wikipedia : Windows Management Instrumentation (WMI)
MSDN : WMI Command Line (WMIC)
Tech Advisor : What's the Best CPU Temperature?
SpeedFan : Fan & Temperature control utility (Freeware)
systeminformation: systeminformation npm package (for nodejs)

For those looking for a PowerShell solution:
Get-CimInstance -Namespace root/wmi -ClassName MsAcpi_ThermalZoneTemperature -Filter "Active='True' and CurrentTemperature<>2732" -Property InstanceName, CurrentTemperature |
Select-Object InstanceName, #{n='CurrentTemperatureC';e={'{0:n0} C' -f (($_.CurrentTemperature - 2732) / 10.0)}}
The WMI/CIM filter here is only looking for active sensors that aren't returning 0 C as the temperature. My system returns several sensors with that value and I assume they're just disabled or otherwise non-functional. The InstanceName property should give you an approximate idea where the sensor is located. In my systems, even though the property is supposed to be in tenths of degrees K, it's always reporting in whole degree values and it appears to round the -273.15 C absolute zero to -273.2. Other systems or sensors may vary.
Note that you must be an administrator to query the MsAcpi_ThermalZoneTemperature class.

My HP laptop has HP specific WMI objects that contains temperature in Celcius units and fan RPM speed objects.
Running this WMIC command in administrator mode will retrieve them:
wmic /NAMESPACE:\\root\HP\InstrumentedBIOS path HPBIOS_BIOSNumericSensor get Name,CurrentReading
Adding for syntax for looping it every 5 seconds:
FOR /L %G IN (1,1,100) DO wmic /NAMESPACE:\\root\HP\InstrumentedBIOS path HPBIOS_BIOSNumericSensor get Name,CurrentReading && PING localhost -l 0 -n 5 >NUL

On my Dell-Laptop I could get the CPU-Temperature in Celsius like this:
$data = Get-WMIObject -Query "SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation" -Namespace "root/CIMV2"
#($data)[0].HighPrecisionTemperature

Related

Control Windows 10's "Power Mode" programmatically

Background
Hi. I have an SB2 (Surface Book 2), and I'm one of the unlucky users who are facing the infamous 0.4GHz throttling problem that is plaguing many of the SB2 machines. The problem is that the SB2 suddenly, and very frequently depending on the ambient temperature, throttles heavily from a boost of 4GHz to 0.4GHz and hangs in there for a minute or two (this causes a severe slowup of the whole laptop). This is extremely frustrating and almost makes the machine unusable for even the simplest of workloads.
Microsoft apparently stated that it fixed the problem in the October 2019 update, but I and several other users are still facing it. I'm very positive my machine is up to date, and I even manually installed all the latest Surface Book 2 firmware updates.
Here's a capture of the CPU state when the problem is happening:
As you can see, the temperature of the unit itself isn't high at all, but CPU is throttling at 0.4GHz exactly.
More links about this: 1 2
Workarounds
I tried pretty much EVERYTHING. Undervolting until freezing screens, disabling BD PROCHOT, disabling power throttling in GPE, messing up with the registry, tuning several CPU/GPU settings. Nothing worked.
You can do only 2 things when the throttling starts:
Wait for it to finish (usually takes a minute or two).
Change the Power Mode in windows 10. It doesn't even matter if you're changing it from "Best performance" to "Best battery life", what matters is that you change it. As soon as you do, throttling completely stops in a couple seconds. This is the only manual solution that worked.
Question
In practice, changing this slider each 10 seconds no matter how heavy the workload is, indefinitely lead to a smooth experience without throttling. Of course, this isn't a feasible workaround by hand.
In theory, I thought that if I could find a way to control this mode programmatically, I might be able to wish this problem goodbye by switching power modes every 10 seconds or so.
I don't mind if it's in win32 (winapi) or a .net thing. I looked a lot, found this about power management, but it seems there's no interface for setting in win32. I could have overlooked it, so here's my question:
Is there any way at all to control the Power Mode in Windows 10 programmatically?
OK... I've been wanting command line or programmatic access to adjust the power slider for a while, and I've run across this post multiple times when looking into it. I'm surprised no one else has bothered to figure it out. I worked it out myself today, motivated by the fact that Windows 11 appears to have removed the power slider from the taskbar and you have to go digging into the Settings app to adjust it.
As previously discussed, in the registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes you can find values "ActiveOverlayAcPowerScheme" and "ActiveOverlayDcPowerScheme" which record the current values of the slider for AC power and battery power, respectively. However, changing these values is not sufficient to adjust the power slider or the system's mode of operation.
Turns out there is an undocumented method in C:\Windows\System32\powrprof.dll called PowerSetActiveOverlayScheme. It takes a single parameter. I "guessed" that it would take a GUID in the same manner that PowerSetActiveScheme does, and it seems to work.
Note — Using an undocumented API is unsupported by Microsoft. This method may break in future Windows releases. It can be used for personal tinkering but I would not suggest using it in any actual production projects.
Here is the C# PInvoke signature:
[DllImportAttribute("powrprof.dll", EntryPoint = "PowerSetActiveOverlayScheme")]
public static extern uint PowerSetActiveOverlayScheme(Guid OverlaySchemeGuid);
It returns zero on success and non-zero on failure.
Calling it is as simple as:
PowerSetActiveOverlayScheme(new Guid("ded574b5-45a0-4f42-8737-46345c09c238"));
It has immediate effect. This particular GUID moved the slider all the way to the right for me and also updated the "ActiveOverlayAcPowerScheme" value in the registry. Using a GUID of all zeros reset the slider to the middle value. You can see what GUID options are available by just observing the values that show up in the registry when you set the power slider to different positions.
There are two methods that can be used to read the current position of the slider. I'm not sure what the difference between them is, they returned the same value each time in my testing.
[DllImportAttribute("powrprof.dll", EntryPoint = "PowerGetActualOverlayScheme")]
public static extern uint PowerGetActualOverlayScheme(out Guid ActualOverlayGuid);
[DllImportAttribute("powrprof.dll", EntryPoint = "PowerGetEffectiveOverlayScheme")]
public static extern uint PowerGetEffectiveOverlayScheme(out Guid EffectiveOverlayGuid);
They also return zero on success and non-zero on failure. They can be called like...
if (PowerGetEffectiveOverlayScheme(out Guid activeScheme) == 0)
{
Console.WriteLine(activeScheme);
}
There is one more method called "PowerGetOverlaySchemes", which I presume can be used to fetch a list of available GUIDs that could be used. It appears to take three parameters and I haven't bothered with figuring it out.
I created a command-line program which can be used to set the power mode, and it can be found at https://github.com/AaronKelley/PowerMode.
Aaron's answer is awesome work, helped me massively, thank you.
If you're anything like me and
don't have Visual Studio at the ready to compile his tool for yourself and/or
don't necessarily want to run an arbitrary executable file off of GitHub (no offence),
you can use Python (3, in this case) to accomplish the same thing.
For completeness' sake, I'll copy over the disclaimer:
Note — Using an undocumented API is unsupported by Microsoft. This method may break in future Windows releases. It can be used for personal tinkering but I would not suggest using it in any actual production projects.
Please also note, that the following is just basic proof-of-concept code!
Getting the currently active Byte Sequence:
import ctypes
output_buffer = ctypes.create_string_buffer(b"",16)
ctypes.windll.powrprof.PowerGetEffectiveOverlayScheme(output_buffer)
print("Current Effective Byte Sequence: " + output_buffer.value.hex())
ctypes.windll.powrprof.PowerGetActualOverlayScheme(output_buffer)
print("Current Actual Byte Sequence: " + output_buffer.value.hex())
On my system, this results in the following values:
Mode
Byte Sequence
Better Battery
77c71c9647259d4f81747d86181b8a7a
Better Performance
00000000000000000000000000000000
Best Performance
b574d5dea045424f873746345c09c238
Apparently Aaron's and my system share the same peculiarity, where the "Better Performance" Byte Sequence is just all zeros (as opposed to the "expected" value of 3af9B8d9-7c97-431d-ad78-34a8bfea439f).
Please note, that the Byte Sequence 77c71c9647259d4f81747d86181b8a7a is equivalent to the GUID 961cc777-2547-4f9d-8174-7d86181b8a7a and b574d5dea045424f873746345c09c238 represents ded574b5-45a0-4f42-8737-46345c09c238.
This stems from the the fact that GUIDs are written down differently than how they're actually represented in memory. (If we assume a GUID's bytes to be written as ABCD-EF-GH-IJ-KLMN its Byte Sequence representation ends up being DCBAFEHGIJKLMN). See https://stackoverflow.com/a/6953207 (particularly the pararaph and table under "Binary encodings could differ") and/or https://uuid.ramsey.dev/en/latest/nonstandard/guid.html if you want to know more.
Setting a value (for "Better Battery" in this example) works as follows:
import ctypes
modes = {
"better_battery": "77c71c9647259d4f81747d86181b8a7a",
"better_performance": "00000000000000000000000000000000",
"best_performance": "b574d5dea045424f873746345c09c238"
}
ctypes.windll.powrprof.PowerSetActiveOverlayScheme(bytes.fromhex(modes["better_battery"]))
For me, this was a nice opportunity to experiment with Python's ctypes :).
Here is a PowerShell version that sets up a scheduled task to toggle the power overlay every minute. It is based off the godsend answers of Michael and Aaron.
The CPU throttling issue has plagued me on multiple Lenovo X1 Yoga laptops (Gen2 and Gen4 models).
# Toggle power mode away from and then back to effective overlay
$togglePowerOverlay = {
$function = #'
[DllImport("powrprof.dll", EntryPoint="PowerSetActiveOverlayScheme")]
public static extern int PowerSetActiveOverlayScheme(Guid OverlaySchemeGuid);
[DllImport("powrprof.dll", EntryPoint="PowerGetActualOverlayScheme")]
public static extern int PowerGetActualOverlayScheme(out Guid ActualOverlayGuid);
[DllImport("powrprof.dll", EntryPoint="PowerGetEffectiveOverlayScheme")]
public static extern int PowerGetEffectiveOverlayScheme(out Guid EffectiveOverlayGuid);
'#
$power = Add-Type -MemberDefinition $function -Name "Power" -PassThru -Namespace System.Runtime.InteropServices
$modes = #{
"better_battery" = [guid] "961cc777-2547-4f9d-8174-7d86181b8a7a";
"better_performance" = [guid] "00000000000000000000000000000000";
"best_performance" = [guid] "ded574b5-45a0-4f42-8737-46345c09c238"
}
$actualOverlayGuid = [Guid]::NewGuid()
$ret = $power::PowerGetActualOverlayScheme([ref]$actualOverlayGuid)
if ($ret -eq 0) {
"Actual power overlay scheme: $($($modes.GetEnumerator()|where {$_.value -eq $actualOverlayGuid}).Key)." | Write-Host
}
$effectiveOverlayGuid = [Guid]::NewGuid()
$ret = $power::PowerGetEffectiveOverlayScheme([ref]$effectiveOverlayGuid)
if ($ret -eq 0) {
"Effective power overlay scheme: $($($modes.GetEnumerator() | where { $_.value -eq $effectiveOverlayGuid }).Key)." | Write-Host
$toggleOverlayGuid = if ($effectiveOverlayGuid -ne $modes["best_performance"]) { $modes["best_performance"] } else { $modes["better_performance"] }
# Toggle Power Mode
$ret = $power::PowerSetActiveOverlayScheme($toggleOverlayGuid)
if ($ret -eq 0) {
"Toggled power overlay scheme to: $($($modes.GetEnumerator()| where { $_.value -eq $toggleOverlayGuid }).Key)." | Write-Host
}
$ret = $power::PowerSetActiveOverlayScheme($effectiveOverlayGuid)
if ($ret -eq 0) {
"Toggled power overlay scheme back to: $($($modes.GetEnumerator()|where {$_.value -eq $effectiveOverlayGuid }).Key)." | Write-Host
}
}
else {
"Failed to toggle active power overlay scheme." | Write-Host
}
}
# Execute the above
& $togglePowerOverlay
Create a scheduled job that runs the above script every minute:
Note that Register-ScheduledJob only works with Windows PowerShell, not PowerShell Core
I couldn't get the job to start without using the System principal. Otherwise gets stuck indefinitely in Task Scheduler with "The task has not run yet. (0x41303)".
Get-Job will show the job in Windows PowerShell, but Receive-Job doesn't return anything even though there is job output in dir $env:UserProfile\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs$taskName\Output. This might be due to running as System while trying to Receive-Job as another user?
I wish -MaxResultCount 0 was supported to hide the job in Get-Job, but alas it is not.
You can see the task in Windows Task Scheduler under Task Scheduler Library path \Microsoft\Windows\PowerShell\ScheduledJobs
It was necessary to have two script blocks, one as command and one as arguments (that gets serialized/deserialized as a string) because PowerShell script blocks use dynamic closures instead of lexical closures and thus referencing one script block from another when creating a new runspace is not readily possible.
The min interval for scheduled tasks is 1 minute. If it turns out that more frequent toggling is needed, might just add a loop in the toggling code and schedule the task only for startup or login.
$registerJob = {
param($script)
$taskName = "FixCpuThrottling"
Unregister-ScheduledJob -Name $taskName -ErrorAction Ignore
$job = Register-ScheduledJob -Name $taskName -ScriptBlock $([scriptblock]::create($script)) -RunEvery $([TimeSpan]::FromMinutes(1)) -MaxResultCount 1
$psSobsSchedulerPath = "\Microsoft\Windows\PowerShell\ScheduledJobs";
$principal = New-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount
$someResult = Set-ScheduledTask -TaskPath $psSobsSchedulerPath -TaskName $taskName -Principal $principal
}
# Run as Administrator needed in order to call Register-ScheduledJob
powershell.exe -command $registerJob -args $togglePowerOverlay
To stop and remove the scheduled job (must use Windows PowerShell):
$taskName = "FixCpuThrottling"
Unregister-ScheduledJob -Name $taskName-ErrorAction Ignore

How to assign Static IP based on MAC Address using powershell/script in Windows?

I would like to write up a script to assign static IP based on mac addresses, as I am having trouble with "USB to ethernet" adapters lose it's IP settings and assign to different interface Names.
I am running on windows 10 environment and have found a wmi script online that I think might work.
Code I am using:
wmic nicconfig where macaddress="0F:98:90:D6:42:92" call EnableStatic ("192.168.1.1"), ("255.255.255.0")
Error output:
"Invalid format.
Hint: = [, ]."
Thanks
Something like this
$netAdapter = Get-WmiObject Win32_NetworkAdapterConfiguration | where {$_.MACAddress -eq '0F:98:90:D6:42:92'}
$netAdapter.EnableStatic("192.168.1.1", "255.255.255.0")

Regular Expressions - Powershell

I am writing a test case script which installs Citrix Receiver, checks whether the store has been created and then uninstalls it.
The way I am uninstalling Receiver is by running this command:
Start-Process "C:\ProgramData\Citrix\Citrix Receiver 4.12\TrolleyExpress.exe" -argumentlist "/silent /uninstall /cleanup" -Wait -PassThru
The path to TrolleyExpress.exe and /uninstall /cleanup arguments are obtained from a registry key value.
I have obtained the value contents and contained it into a variable:
$uninstallString = (Get-ItemProperty -path HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\CitrixOnlinePluginPackWeb -name UninstallString).UninstallString
$uninstallString becomes C:\ProgramData\Citrix\Citrix Receiver 4.12\TrolleyExpress.exe /uninstall /cleanup
What I'm aiming for is to get the path to TrolleyExpress.exe from the regedit directly, since it is a test case which will run on multiple machines. In case the Receiver is installed on something different than C: or different path.
This is my first month into PowerShell Scripting and I'm quite new to Regular Expressions.
Any help\suggestion on how this could be solved is highly appreciated!
Assuming the path in the registry follows the example you give (except possibly the drive letter), then one easy way to get just the path is:
$exePath = $uninstallString.Split('/')[0]

Get Machine id of MAC OS X

I want machine unique id such as processor id, hdd id, uuid of MAC PC through c++ program.
Can anyone please tell me how it implements?
Thanks.
only about 7 years later, but here's an answer to those stumbling across this that we've been using.
It uses the IOPlatformExpertDevice class to access the Mac Serial number/hardware uuid
There are two ways to do this, the first uses C++, the second python. I have personally used the second way, and can verify it fetches the hardware uuid as given by System Information.
First method, not tested by myself, but uses the same class so has at least the potential to work, see https://gist.github.com/tmiz/1294978 for a routine in C++ on how to retrieve the "serial number" which is not be the same as the hardware uuid from system information, but from some tweaking, you should be able to get the hardware uuid.
Second method (see python code below), in python, which uses the ioreg command, which is executed via a separate process, then the results processed with a regular expression to get the uuid. This method definitely retrieves the hardware uuid as I've checked it with the System Information app in macos 10.14 and previous versions of 10.13 and 10.12.
May these methods serve you well, they do not return the mac address and as such should function well as a uuid for the machine, not just the network interface.
Finally you can read about ioreg here -> http://www.manpagez.com/man/8/ioreg/ and the I/O Kit more generally here -> https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/Families_Ref/Families_Ref.html#//apple_ref/doc/uid/TP0000021-BABHIGFE
import platform, re, os
os_type = platform.system()
if os_type == 'Darwin':
machine_uuid_str = ''
p = os.popen('ioreg -rd1 -c IOPlatformExpertDevice | grep -E \'(UUID)\'', "r")
while 1:
line = p.readline()
if not line: break
machine_uuid_str += line
match_obj = re.compile('[A-Z,0-9]{8,8}-' +\
'[A-Z,0-9]{4,4}-' +\
'[A-Z,0-9]{4,4}-' +\
'[A-Z,0-9]{4,4}-' +\
'[A-Z,0-9]{12,12}')
results = match_obj.findall(machine_uuid_str)
return results[0]
Outside of a few ancient processors, x86 CPUs do not have software-visible serial numbers.
Apple recommends that you use the MAC address of the computer's primary network interface (i.e, the onboard Ethernet controller if present, or the wireless interface otherwise) as a unique identifier for the system. Sample code for doing this is available in Apple's Validating Mac App Store Receipts documentation (under "Get the Computer's GUID").

Identify a mounted volume as a CD/DVD on osx

When you mount anything on osx it shows up under /Volumes/mountname
Is there any way, using the commandline or C/C++ to identify the volume as a CD/DVD rom?
My current best idea goes something like this.
df | grep mountname
to get the /dev/diskNsM path
and then
drutil | grep /dev/diskN
to see if the path mounted device is the burner.
This works, but i'm concerned about the case where the CD/DVD is not a burner. Will it still show up in the output of drutil? Do macs even come with a non-burner CD/DVD drive?
Also i would prefer using C,C++ or objective C to do this.
I already use
const char *tmp = '/Volumes/mysterydrive';
statfs(tmp, &m);
if(m.f_flags & MNT_RDONLY)
{
read_only = true;
}
to determine if the volume is readonly,
but i cannot see if this or any related call can distinguish between a CD/DVD and a readonly mounted volume.
It would only need to work for OSX 10.5 and newer.
Any ideas?
EDIT:
Using
diskutil info /Volumes/mysterydrive
I got the following output if its a CD/DVD
Optical Drive Type: CD-ROM, CD-R, CD-RW, DVD-ROM, DVD-R, DVD-R DL, DVD-RW, DVD+R, DVD+R DL, DVD+RW
Optical Media Type: DVD-R
Optical Media Erasable: No
And that's all i need!
I'll look into using IOKit to do it programmatically later, but this seems to be the quickest way to get it done.
You get the most detailed information from
diskutil info /Volume/foo
In particular see Optical Drive and Optical Media entries which you only get for CD/DVDs so it is quite reliable.
Unfortunately the frameworks that diskutil uses to get all that information are private, so it will be hard to replicate it in C code.
I didn't dig deeper into other options, but since you can get the disk name from statfs it may be in theory possible to use IOKit to check out the device and you'll see IOCDMedia or IODVDMedia class if it is a CD/DVD drive (i.e. if you look for IO*Media class the BSD Name property has the disk name like disk6)
Apple Developer Central has a sample code project called CDROMSample that shows using IOKit routines to access CD-ROM properties. This might give you a start into handling DVDs, as well.