The seemingly simple problem

I could not believe what a total pain in the ass this was to resolve. I tried getting the CPU and Memory utilization via CMD but that produced incredibly unreliable results. The biggest problem being that division operations in CMD will only return integers so I was getting very weird results. Yes there is a way around this, but it was more of a hassle so I gave up and looked to PowerShell to save the day when CMD fails. However PowerShell has its own problems, there was a reason I was using CMD and that was because I needed to be able to call a batch file from a program that would only execute CMD commands.

The somewhat simple solution

The solution here, once all is said and done, is pretty simple – but getting to this point was NOT simple. There was a lot of frankensteining, google coding and asking coworkers questions. Anyhow below is the solution for those of you who just need to make things work, I am going to explain each part below the code for those interested in the break down.

Batch file component

You need this to call the PowerShell script from CMD in a single execution.

@echo off

REM Get the current directory where this is being executed
set ThisScriptsDirectory=%~dp0

REM Get the name of the current batch file and append .ps1 to the name
set PowerShellScriptPath=%~dpn0.ps1

REM execute the target powershell file
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" ' -Verb RunAs}";

PowerShell component

If you just want to run the PowerShell script, then this is all you need.

$c = $env:computername #Use the current computer
$path = "C:\SaveYourReadingHere\Reading.txt" #Manually specify where you are saving your data, I tried getting the current directory but it was more complicated than necessary

#I borrowed elements of this elegant solution from here: https://stackoverflow.com/a/18091659/603807
$avg = Get-WmiObject win32_processor -computername $c | 
Measure-Object -property LoadPercentage -Average | 
Foreach {$_.Average}
$mem = Get-WmiObject win32_operatingsystem -ComputerName $c |
Foreach {"{0:N2}" -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/ $_.TotalVisibleMemorySize)}

#In my case I needed the data to be a CSV
$a = ($avg.ToString() + "," + $mem.ToString())

#Write data to a file, the encoding part here is very important otherwise your file may not be readable by some programs
Out-File -FilePath $path -InputObject $a -Encoding ASCII

How does this work?

The idea here is that you are going to call the batch file which is going to call the PowerShell script, but there is only one caveat with the way I have things configured. You have to name your batch file the same thing as your PowerShell script to make this seamless. This honestly just makes life easier. Do you have to do it this way? No, but again it’s just easier.

Example:

  1. Create a batch file named “GetCpuAndMemoryReadings.bat”
  2. Create a PowerShell script named “GetCpuAndMemoryReadings.ps1”
    1. Make sure to explicitly specify where you are saving your readings file. I used a flat file to save the results because propagating the output back to CMD was a total paint in the ass. Again not worth the time to figure it out (like a lot of things in PowerShell I find is the theme).
  3. When you run the batch file from CMD your reading will be outputted to the flat file.
  4. If you have another process that needs to consume the data, read the flat file you just produced

Credit where credit is due

  • The PowerShell script is largely adapted from this SO answer: https://stackoverflow.com/a/18091659/603807
  • My coworker gave me the batch file solution, I had no idea you could do that – it is very useful for calling PowerShell scripts