Get CPU temperature in CMD/POWER Shell - powershell

In my computer I am trying to get the CPU temperature. Searching on StackOverflow I found this:
C:\WINDOWS\system32>wmic /namespace:\\root\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature
But I get this error:
Node - ADMIN
ERROR:
Description = Not supported

you can use this code :
function Get-Temperature {
$t = Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi"
$returntemp = #()
foreach ($temp in $t.CurrentTemperature)
{
$currentTempKelvin = $temp / 10
$currentTempCelsius = $currentTempKelvin - 273.15
$currentTempFahrenheit = (9/5) * $currentTempCelsius + 32
$returntemp += $currentTempCelsius.ToString() + " C : " + $currentTempFahrenheit.ToString() + " F : " + $currentTempKelvin + "K"
}
return $returntemp
}
Get-Temperature

You can use Open Hardware Monitor it's an open source software (MPL v2).
You can access the command line version here:
OpenHardwareMonitorReport.zip
Example part of the output:
PS C:\Users\myuser\OpenHardwareMonitorReport> .\OpenHardwareMonitorReport.exe
Open Hardware Monitor Report
--------------------------------------------------------------------------------
Version: 0.8.0.2
--------------------------------------------------------------------------------
Common Language Runtime: 4.0.30319.42000
Operating System: Microsoft Windows NT 6.2.9200.0
Process Type: 32-Bit
--------------------------------------------------------------------------------
Sensors
|
+- HP 00F52W (/mainboard)
|
+- Intel Core i7-3770 (/intelcpu/0)
| +- Bus Speed : 99.7734 99.7734 99.7784 (/intelcpu/0/clock/0)
| +- CPU Core #1 : 3691.62 3691.62 3791.58 (/intelcpu/0/clock/1)
| +- CPU Core #2 : 3691.62 3691.62 3791.58 (/intelcpu/0/clock/2)
| +- CPU Core #3 : 3791.39 3791.39 3891.36 (/intelcpu/0/clock/3)
| +- CPU Core #4 : 3691.62 3691.62 3891.36 (/intelcpu/0/clock/4)
| +- CPU Core #1 : 42 42 43 (/intelcpu/0/temperature/0)
| +- CPU Core #2 : 43 37 43 (/intelcpu/0/temperature/1)
| +- CPU Core #3 : 42 35 42 (/intelcpu/0/temperature/2)
| +- CPU Core #4 : 45 41 45 (/intelcpu/0/temperature/3)
| +- CPU Package : 45 43 45 (/intelcpu/0/temperature/4)
Open Hardware Monitor Official website
link to issue where the command line version is
linked: #776
Pending pull request with more recent version: https://github.com/openhardwaremonitor/openhardwaremonitor/pull/1115#issuecomment-616230088

Run the following command in Command Prompt as an Administrator:
wmic /namespace:\\root\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature
This will give you some output like this:
CurrentTemperature 3000 3010
But make sure that you are running the cmd as an Administrator

On my laptop all above gave me wrong results. Only this one was showing the CPU-Temperature in Celsius:
$data = Get-WMIObject -Query "SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation" -Namespace "root/CIMV2"
#($data)[0].HighPrecisionTemperature
I guess, that each CPU-version may have a different place/formular to get the correct CPU-temperature.

With new sensor, or with what I have and with elevation.
It also shows critical temperature and percentage (in Celsius)
It leaves a file Temperatures.txt for easy debugging, and the xml with serialized object from sensors
function Get-Temperature {
$TempFormat = "#"
$TempFile = "temperature"
$Command = 'Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi" ' + " > $pwd\$TempFile.txt"
$Command = 'Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi" ' + " | Export-Clixml $pwd\$TempFile.xml"
$p = Start-Process -Verb runas -FilePath "powershell" -ArgumentList $command -WorkingDirectory $pwd -PassThru
$p.WaitForExit()
$t = Import-Clixml pippo.xml
$returntemp = #()
foreach ($Sensor in $t)
{
$Active = if($sensor.Active){"On "}else{"Off"}
$temp = $Sensor.CurrentTemperature
$Critical = $Sensor.CriticalTripPoint
$currentTempKelvin = $temp / 10
$currentTempCelsius = $currentTempKelvin - 273.15
$currentTempFahrenheit = (9/5) * $currentTempCelsius + 32
$StrKelvin = $currentTempKelvin.ToString($TempFormat).PadLeft(3, " ")
$StrCelsius = $currentTempCelsius.ToString($TempFormat).PadLeft(3, " ")
$StrFahrenheit = $currentTempFahrenheit.ToString($TempFormat).PadLeft(3, " ")
$CriticalKelvin = $Critical / 10
$CriticalCelsius = $CriticalKelvin - 273.15
$CriticalFahrenheit = (9/5) * $CriticalCelsius + 32
$StrCritKelvin = $CriticalKelvin.ToString($TempFormat).PadRight(3, " ")
$StrCritCelsius = $CriticalCelsius.ToString($TempFormat).PadRight(3, " ")
$StrCritFahrenheit = $CriticalFahrenheit.ToString($TempFormat).PadRight(3, " ")
$PerCrit = ($currentTempCelsius/$CriticalCelsius * 100)
$StrPerCrit = $PerCrit.ToString($TempFormat).PadLeft(3, " ")
$returntemp += "$Active $StrPerCrit% $StrCelsius/$StrCritCelsius C : $StrFahrenheit/$StrCritFahrenheit F : $StrKelvin/$StrCritKelvin K - " + $Sensor.InstanceName
}
return $returntemp
}
Get-Temperature

According to the answer on this question :
To get the exact temperature of CPU (and every core) you need to write
kernel drivers, what is much more complicated.
CurrentTemperature returns temperature at some thermal zone which is
somewhere on motherboard.
This would explain why some of the answers on this page return a temperature, but it's wildly different to the actual CPU temp.

Related

Results output to txt file by group

I'm currently studying Powershell and working on a script that grabs multi-monitors Display configuration from windows system. Thanks to the help from other guys, I finally work out a full script to show each monitor's resolution, Video output category, Manufacturer, and Model. Here is the full script:
add-type -assemblyName system.windows.forms
[system.windows.forms.screen]::AllScreens.Bounds | format-list Width,Height |
out-file -append system1.txt
enum VideoOutputTechnology {
UNINITIALIZED = -2
UNKNOW = -1
VGA = 0
S_VIDEO = 1
COMPOSITE_VIDEO = 2
COMPONENT_VIDEO = 3
DVI = 4
HDMI = 5
LVDS_OR_MIPI_DSI = 6
D_JPN = 8
SDI = 9
DISPLAYPORT_EXTERNAL = 10
DISPLAYPORT_EMBEDDED = 11
UDI_EXTERNAL = 12
UDI_EMBEDDED = 13
DONGLE_CABLE_THAT_SUPPORTS_SDTV = 14
MIRACAST_CONNECTED_SESSION = 15
INTERNAL_CONNECTION = 0x80000000
}
Get-WmiObject WmiMonitorconnectionparams -Namespace root\wmi |
Format-List #{
n='VideoOutputTechnology'
e={ [VideoOutputTechnology] $_.VideoOutputTechnology }
} >> system1.txt
Get-WmiObject WmiMonitorID -Namespace root\wmi | ForEach-Object {
[PSCustomObject]#{
Manufacturer = ($_.ManufacturerName | ForEach {[char]$_}) -join ""
Model = ($_.UserFriendlyName | ForEach {[char]$_}) -join ""
}
} | Format-List | Out-File -append system1.txt
The output in system1.txt is:
Width : 1920
Height : 1080
Width : 2560
Height : 1440
VideoOutputTechnology : DISPLAYPORT_EXTERNAL
VideoOutputTechnology : DVI
Manufacturer : ACI
Model : ASUS PB287Q
Manufacturer : SAM
Model : SMS27A850
My question is: How could I make the output results group by each monitor.
For example:
Manufacturer: ACI
Model: ASUS PB287Q
Width: 1920
Height: 1080
VideoOutputTechnology: DISPLAYPORT_EXTERNAL
Manufacturer: SAM
Model: SMS27A850
Width: 2560
Height: 1440
VideoOutputTechnology: DVI
Thanks in advance for everyone's reply.
Put the partial results into variables and assemble all in one [PSCustomObject]
(I'm not quite sure if the order of the screens will match the MonitorID order)
The property InstanceName which is common to both WmiMonitorID and WmiMonitorconnectionparams is used to sync the settings.
EDIT: incorporated mklement0's hint
EDIT2: added properties ProductCodeID,SerialNumberID,Manufactured(year,week)
## Q:\Test\2018\07\16\SO_51368476.ps1
##
enum VideoOutputTechnology {
UNINITIALIZED = -2
UNKNOW = -1
VGA = 0
S_VIDEO = 1
COMPOSITE_VIDEO = 2
COMPONENT_VIDEO = 3
DVI = 4
HDMI = 5
LVDS_OR_MIPI_DSI = 6
D_JPN = 8
SDI = 9
DISPLAYPORT_EXTERNAL = 10
DISPLAYPORT_EMBEDDED = 11
UDI_EXTERNAL = 12
UDI_EMBEDDED = 13
DONGLE_CABLE_THAT_SUPPORTS_SDTV = 14
MIRACAST_CONNECTED_SESSION = 15
INTERNAL_CONNECTION = 0x80000000
}
Add-Type -AssemblyName system.windows.forms
$Screens = [system.windows.forms.screen]::AllScreens.Bounds
$MIDs = Get-WmiObject WmiMonitorID -Namespace root\wmi
$i=0
$AllMonInfo = ForEach ($MID in $MIDs){
$MCP = (Get-WmiObject WmiMonitorconnectionparams -Namespace root\wmi |
Where-Object InstanceName -eq $MID.InstanceName)
[PSCustomObject]#{
Manufacturer = (-join [char[]] $MID.ManufacturerName)
Model = (-join [char[]] $MID.UserFriendlyName)
ProductCodeID= (-join [char[]] $MID.ProductCodeID)
SerialNumberID=(-join [char[]] $MID.SerialNumberID)
Manufactured = ("{0}W{1}" -f $MID.YearOfManufacture,$Mid.WeekOfManufacture.ToString('00'))
Width = $Screens[$i].Width
Height = $Screens[$i].Height
VideoOutput = ([VideoOutputTechnology]$MCP.VideoOutputTechnology)
}
$i++
}
$AllMonInfo | Format-List | Out-File '.\system1.txt' -Append -Encoding ascii
Sample output:
Get-Content '.\system1.txt'
Manufacturer : SAM
Model : SyncMaster
ProductCodeID : 03E7
SerialNumberID : H9XQxxxxxx
Manufactured : 2008W40
Width : 1920
Height : 1200
VideoOutput : DVI
Manufacturer : SAM
Model : SyncMaster
ProductCodeID : 0425
SerialNumberID : H1AKxxxxxx
Manufactured : 2008W10
Width : 1920
Height : 1200
VideoOutput : DVI

\network adapter(*)\bytes total/sec vs. task manager/performance -powershell

On Windows 10, in task manager/performance it gives the values for 'Receive' and 'Send' for network adapters. I got curious as to how MS does this so I thought of performance counters in powershell. So I cobbled together a quick bit of code to experiment with, put it in a background job, this:
$Computer = hostname;
$sndrecsum = {((get-counter -Counter '\network adapter(*)\Bytes Received/sec' -MaxSamples 30).CounterSamples | measure cookedvalue –Sum).Sum};
$sptstb = New-Object System.Net.WebClient;
$sptstb.DownloadFile('http://client.akamai.com/install/test-objects/10MB.bin', 'Out-Null');
Start-Job -Name sxrxs -scriptblock $sndrecsum -ArgumentList $Computer;
Get-Job -Name sxrxs | Wait-Job; $sndrecsumout = Get-Job -Name sxrxs | Receive-Job; ($sndrecsumout | measure -Maximum).Maximum
here's the issue. The output '($sndrecsumout | measure -Maximum).Maximum' should be getting the maximum value in the array $sndrecsumout. It does do that but the max value in the array is always wayyyy lower than it should be and never matches that shown in task manager/performance for 'Receive' and 'Send' (after conversion to the prevailing units shown in task manager/performance at the same time). So i'm not sure if task manager/performance is using performance counters or if i'm screwing up somewhere.
Anyway, two questions: First, how does task manager/performance get the values for 'Receive' and 'Send'? - and - Two, am I screwing up with the way i'm trying to do this?
Thanks for replies and advice in advance.
ok, think i've got it figured out here, at least a little, maybe. This tracks with the Windows 10 performance monitor for the interface;
$sptst = New-Object System.Net.WebClient; $Computer = hostname; $strlf += $("" | Out-String);
$totx = {
get-counter -Counter '\Network Interface(Intel[R] Ethernet Connection [2] I218-V)\Bytes Total/sec' -MaxSamples 40 | ForEach {[math]::Round((($_.countersamples.cookedvalue | measure -sum).sum / 1Mb), 4)}
};
$recx = {
get-counter -Counter '\network Interface(Intel[R] Ethernet Connection [2] I218-V)\Bytes Received/sec' -MaxSamples 40 | ForEach {[math]::Round((($_.countersamples.cookedvalue | measure -sum).sum / 1Mb), 4)}
};
$sntx = {
get-counter -Counter '\network Interface(Intel[R] Ethernet Connection [2] I218-V)\Bytes Sent/sec' -MaxSamples 40 | ForEach {[math]::Round((($_.countersamples.cookedvalue | measure -sum).sum / 1Mb), 4)}
};
Start-Job -Name xtot -scriptblock $totx -ArgumentList $Computer;
Start-Job -Name xrec -scriptblock $recx -ArgumentList $Computer;
Start-Job -Name xsnt -scriptblock $sntx -ArgumentList $Computer;
$ts = Measure-Command -Expression {$sptst.DownloadFile('http://testmy.net/dl-10MB', 'Out-Null')}; $dxtmex = [math]::Round($ts.TotalSeconds, 4);
Get-Job -Name xtot -HasMoreData $True | Wait-Job ; $outxtot = Get-Job -Name xtot | Receive-Job;
$xthrx = ($outxtot | measure -Maximum).Maximum; $testnuma = $xthrx -match '^[\d\.]+$'; if ($testnuma -eq 'True') {$MBxthrx = $xthrx; $Mbpsxthrx = $xthrx / 0.125};
Get-Job -Name xrec -HasMoreData $True | Wait-Job; $outxrec = Get-Job -Name xrec | Receive-Job;
$xrecx = ($outxrec | measure -Maximum).Maximum ; $testnumb = $xrecx -match '^[\d\.]+$'; if ($testnumb -eq 'True') {$MBxrecx = $xrecx; $Mbpsxrecx = $xrecx / 0.125};
Get-Job -Name xsnt -HasMoreData $True | Wait-Job; $outxsnt = Get-Job -Name xsnt | Receive-Job;
$xsntx = ($outxsnt | measure -Maximum).Maximum ; $testnumb = $xsntx -match '^[\d\.]+$'; if ($testnumb -eq 'True') {$MBxsntx = $xsntx; $Mbpsxsntx = $xsntx / 0.125};
$txtl = '|Throughput = ' + $Mbpsxthrx + ' Mbps (' + $MBxthrx + ' MB/s) [ Test Time = ' + $dxtmex + ' seconds ]';
$rxsx = 'Received (D/L) : ' + $Mbpsxrecx + ' Mbps (' + $MBxrecx + ' MB/s)' + ' / Sent (U/L) : ' + $Mbpsxsntx + ' Mbps (' + $MBxsntx + ' MB/s)';
Return $txtl + $strlf + $rxsx
Edit: removed previous answer - this one is much better and improved. Ignore the labels 'Throughput' - 'Received (D/L)' - 'Sent (U/L)' as just called them something to differentiate while playing around with it so call them anything you wish if desired. 'Throughput' in the windows performance monitor is expressed as a percentage and not like this anyway but the results expressed in 'Throughput' above code when converted for percentage in math (not shown in above code) seem to track with the perf monitor which is why I chose the label 'Throughput' here. Also during this figured out how to get the correct instance name for the interface for use in the counters, I don't show its use (or the code for it) this post, and just put the interface name in above code, to keep strictly on topic but if someone wants to see it and its OK to do i'll post it here also. I'll just probably come back later and post it anyway if its Ok in this post and if not ya'll can remove it if you wish if it would be off topic some. Any improvements/suggestions welcome.
Thank You.

Extract columns from text based table output

qfarm /load command shows me the load from my servers.
Output:
PS> qfarm /load
Server Name Server Load Load Throttling Load Logon Mode
-------------------- ----------- -------------------- ------------------
SERVER-01 400 0 AllowLogons
SERVER-02 1364 OFF AllowLogons
SERVER-03 1364 OFF AllowLogons
SERVER-04 1000 0 AllowLogons
SERVER-05 700 0 AllowLogons
SERVER-06 1200 0 AllowLogons
I need to display only first column (Server Name) and the second one (Server Load) and loop through them, in order to make some logic later, but it seems the powershell doesn't see it as object with properties:
PS> qfarm /load | Select -ExpandProperty "Server Name"
Select-Object : Property "Server Name" cannot be found.
Is there any other possibility, like a table or something?
One way to do this is to build objects out of the command's output. Tested the following:
#requires -version 3
# sample data output from command
$sampleData = #"
Server Name Server Load Load Throttling Load Logon Mode
-------------------- ----------- -------------------- ------------------
SERVER-01 400 0 AllowLogons
SERVER-02 1364 OFF AllowLogons
SERVER-03 1364 OFF AllowLogons
SERVER-04 1000 0 AllowLogons
SERVER-05 700 0 AllowLogons
SERVER-06 1200 0 AllowLogons
"# -split "`n"
$sampleData | Select-Object -Skip 2 | ForEach-Object {
$len = $_.Length
[PSCustomObject] #{
"ServerName" = $_.Substring(0, 22).Trim()
"ServerLoad" = $_.Substring(22, 13).Trim() -as [Int]
"LoadThrottlingLoad" = $_.Substring(35, 22).Trim()
"LogonMode" = $_.Substring(57, $len - 57).Trim()
}
}
In your case, you should be able to replace $sampleData with your qfarm load command; e.g.:
qfarm /load | Select-Object -Skip 2 | ForEach-Object {
...
Of course, this is assuming no blank lines in the output and that my column positions for the start of each item is correct.
PowerShell version 2 equivalent:
#requires -version 2
function Out-Object {
param(
[Collections.Hashtable[]] $hashData
)
$order = #()
$result = #{}
$hashData | ForEach-Object {
$order += ($_.Keys -as [Array])[0]
$result += $_
}
New-Object PSObject -Property $result | Select-Object $order
}
# sample data output from command
$sampleData = #"
Server Name Server Load Load Throttling Load Logon Mode
-------------------- ----------- -------------------- ------------------
SERVER-01 400 0 AllowLogons
SERVER-02 1364 OFF AllowLogons
SERVER-03 1364 OFF AllowLogons
SERVER-04 1000 0 AllowLogons
SERVER-05 700 0 AllowLogons
SERVER-06 1200 0 AllowLogons
"# -split "`n"
$sampleData | Select-Object -Skip 2 | ForEach-Object {
$len = $_.Length
Out-Object `
#{"ServerName" = $_.Substring(0, 22).Trim()},
#{"ServerLoad" = $_.Substring(22, 13).Trim() -as [Int]},
#{"LoadThrottlingLoad" = $_.Substring(35, 22).Trim()},
#{"LogonMode" = $_.Substring(57, $len - 57).Trim()}
}
You can easily convert your table to PowerShell objects using the ConvertFrom-SourceTable cmdlet from the PowerShell Gallery:
$sampleData = ConvertFrom-SourceTable #"
Server Name Server Load Load Throttling Load Logon Mode
-------------------- ----------- -------------------- ------------------
SERVER-01 400 0 AllowLogons
SERVER-02 1364 OFF AllowLogons
SERVER-03 1364 OFF AllowLogons
SERVER-04 1000 0 AllowLogons
SERVER-05 700 0 AllowLogons
SERVER-06 1200 0 AllowLogons
"#
And than select your columns like:
PS C:\> $SampleData | Select-Object "Server Name", "Server Load"
Server Name Server Load
----------- -----------
SERVER-01 400
SERVER-02 1364
SERVER-03 1364
SERVER-04 1000
SERVER-05 700
SERVER-06 1200
For details see: ConvertFrom-SourceTable -?
The ConvertFrom-SourceTable cmdlet is available for download at the PowerShell Gallery and the source code from the GitHub iRon7/ConvertFrom-SourceTable repository.
Command-line utilities return their outputs as a string array. This should work:
qfarm /load | ForEach-Object { $_.Substring(0,33) }
I have answered something very similar to this in the past. I have a larger function for this but a simplified on work on left aligned string table just as you have shown in you example. See the linked answer for more explanation.
function ConvertFrom-LeftAlignedStringData{
param (
[string[]]$data
)
$headerString = $data[0]
$headerElements = $headerString -split "\s{2,}" | Where-Object{$_}
$headerIndexes = $headerElements | ForEach-Object{$headerString.IndexOf($_)}
$results = $data | Select-Object -Skip 2 | ForEach-Object{
$props = #{}
$line = $_
For($indexStep = 0; $indexStep -le $headerIndexes.Count - 1; $indexStep++){
$value = $null # Assume a null value
$valueLength = $headerIndexes[$indexStep + 1] - $headerIndexes[$indexStep]
$valueStart = $headerIndexes[$indexStep]
If(($valueLength -gt 0) -and (($valueStart + $valueLength) -lt $line.Length)){
$value = ($line.Substring($valueStart,$valueLength)).Trim()
} ElseIf ($valueStart -lt $line.Length){
$value = ($line.Substring($valueStart)).Trim()
}
$props.($headerElements[$indexStep]) = $value
}
New-Object -TypeName PsCustomObject -Property $props
}
return $results
}
$qfarmOutput = qfarm /load
ConvertFrom-LeftAlignedStringData $qfarmOutput | select "Server Name","Server Load"
This approach is based on the position of the header fields. Nothing is hardcoded and it is all custom built based on those indexes and field names. Using those $headerIndexes we carve up every line and place the results, if present, into its respective column. There is logic to ensure that we don't try and grab any part of the string that might not exist and treat the last field special.
Results
Server Name Server Load
----------- -----------
SERVER-01 400
SERVER-02 1364
SERVER-03 1364
SERVER-04 1000
SERVER-05 700
SERVER-06 1200

Remove Symantec Endpoint Protection in an Enterprise Environment

I have many servers I need to remove Symantec Endpoint Protection from. I contacted Symantec and received the code below:
(Get-WmiObject -Class Win32_Product -Filter "Name='Symantec Endpoint Protection'" -ComputerName xxxxxx).Uninstall()
I have used it and it worked on 10 servers no problem at all. I tried it again today and am getting the error:
You cannot call a method on a null-valued expression.
At line:1 char:1
+ (Get-WmiObject -Class Win32_Product -Filter "Name='Symantec Endpoint Protection' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
Nothing has changed from when I started and am trying to figure out what the above error means. Also if I can get this to work does anyone see a way to add many servers to a foreach command or something.
Two things from my side:
1) you should not use win32_product cause it is broken to a certain level.
It is very slow alsso because it scans the entire thing.
2) In your case, "Name='Symantec Endpoint Protection'" reports Null for you which means that the value is not there. Please check the proper name.
For better performance and as part of enhancement , you should use the registry to fetch the details.
Function Get-RemoteSoftware{
<#
.SYNOPSIS
Displays all software listed in the registry on a given computer.
.DESCRIPTION
Uses the SOFTWARE registry keys (both 32 and 64bit) to list the name, version, vendor, and uninstall string for each software entry on a given computer.
.EXAMPLE
C:\PS> Get-RemoteSoftware -ComputerName SERVER1
This shows the software installed on SERVER1.
#>
param (
[Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
# Specifies the computer name to connect to
$ComputerName
)
Process {
foreach ($Computer in $ComputerName)
{
#Open Remote Base
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
#Check if it's got 64bit regkeys
$keyRootSoftware = $reg.OpenSubKey("SOFTWARE")
[bool]$is64 = ($keyRootSoftware.GetSubKeyNames() | ? {$_ -eq 'WOW6432Node'} | Measure-Object).Count
$keyRootSoftware.Close()
#Get all of they keys into a list
$softwareKeys = #()
if ($is64){
$pathUninstall64 = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$keyUninstall64 = $reg.OpenSubKey($pathUninstall64)
$keyUninstall64.GetSubKeyNames() | % {
$softwareKeys += $pathUninstall64 + "\\" + $_
}
$keyUninstall64.Close()
}
$pathUninstall32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$keyUninstall32 = $reg.OpenSubKey($pathUninstall32)
$keyUninstall32.GetSubKeyNames() | % {
$softwareKeys += $pathUninstall32 + "\\" + $_
}
$keyUninstall32.Close()
#Get information from all the keys
$softwareKeys | % {
$subkey=$reg.OpenSubKey($_)
if ($subkey.GetValue("DisplayName")){
$installDate = $null
if ($subkey.GetValue("InstallDate") -match "/"){
$installDate = Get-Date $subkey.GetValue("InstallDate")
}
elseif ($subkey.GetValue("InstallDate").length -eq 8){
$installDate = Get-Date $subkey.GetValue("InstallDate").Insert(6,".").Insert(4,".")
}
New-Object PSObject -Property #{
ComputerName = $Computer
Name = $subkey.GetValue("DisplayName")
Version = $subKey.GetValue("DisplayVersion")
Vendor = $subkey.GetValue("Publisher")
UninstallString = $subkey.GetValue("UninstallString")
InstallDate = $installDate
}
}
$subkey.Close()
}
$reg.Close()
}
}
}
Note: Use the function or the query inside the function to get the result.
Hope it helps.

Powershell scripting methodargumentconversioninvalidargument

I am a newbie to Powershell scripting and I want to find the standard deviation of the numbers I have in the results variable.
Following is my code:
param(
## The command to measure
[Scriptblock] $Scriptblock,
## The number of times to measure the command's performance
[int] $Iterations = 2
)
Set-StrictMode -Version Latest
## Figure out how many extra iterations we need to account for the outliers
$buffer = [int] ($iterations * 0.1)
$totalIterations = $iterations + (2 * $buffer)
## Get the results
$results = 1..$totalIterations |
Foreach-Object { Measure-Command $scriptblock }
## Sort the results, and skip the outliers
$middleResults = $results | Sort TotalMilliseconds |
Select -Skip $buffer -First $iterations
$popdev = 0
## Show the average
$avg = $results | Measure-Object -Average | select Count, Average
$middleResults | Measure-Object -Average TotalMilliseconds
foreach ($number in $results){
$popdev += [math]::pow(($number - $avg.Average), 2)
}
I get the following error:
Count : 2
Average : 351.1846
Sum :
Maximum :
Minimum :
Property : TotalMilliseconds
Cannot convert argument "1", with value: "", for "op_Subtraction" to type "System.TimeSpan": "Cannot convert null to
type "System.TimeSpan"."
At C:\script.ps1:55 char:3
+ $popdev += [math]::pow(($number - $avg.Average), 2)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
The first problem that I see (which is where the error is coming from) is that $results is a collection of Timespan objects (because that's what measure-command outputs).
Second, you didn't specify what property to Average when dealing with $middleresults.
Third, you didn't assign the pipeline starting with $middleresults to anything, so it is simply output. I'm not sure what you want to do with this result.
Because $results are Timespan objects, inside the pow function, when you use $number by itself you get an error subtracting the averages. If you use $number.TotalMilliseconds, I think you'll get what you're expecting.