Introduction
Since I use a homebuilt PC, I wanted a simple application to display my CPU temperature unobstrusively on my desktop.
Background
This project started when I wanted to write a small utility to display the CPU temperature in a corner of the desktop using the open source OpenHardwareMonitorLib.dll (). Web Page here. This library provides extensive hardware information, including multiple system temperatures accessible through a Windows Management Interface (WMI). However, I found the limited documentation available somewhat confusing and had better luck using the public
class enumerations provided by OHM directly, the key being to call the Update
methods correctly for items and subitems before attempting to enumerate them.
Once I got it working, I created a wrapper class, OHData
which has one public
property, DataList
and one public
method, Update
.
Since I was not able to extract BIOS information from OHM (even though it is visible when debugging), I also created a small separate wrapper class, WMBIOS
, which gets the relevant information using System.Management
(add this reference to your project as well).
Here are samples from the demo application:
The temperature window:
The system information popup window:
Using the Code
To use the code, include a copy of OpenHardwareMonitorLib.dll in your project, add a reference to it. Then create a local instance of OHdata
, for example OHData od = new OHData()
, call the update
method and get the results in a List<OHMItem>
object, each OHItem
consisting of a Sensor Type, Sensor Name, and reading (all string
s). The OHMItem
class structure is a public
member of OHData
. OHItems
in the retrieved list are preformatted for most types, such as voltages, temperatures, and fan speeds, ready to use in a ListView
. To get just the CPU temperature, you can search the List
for Type = "Temperature"
and Name = "CPU"
if you just want to use that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Reflection;
using System.IO;
using System.Management;
using OpenHardwareMonitor;
using OpenHardwareMonitor.Collections;
using OpenHardwareMonitor.Hardware;
namespace <your project namespace goes here>
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
public class OHData
{
public List<ohmitem> DataList
{
get
{
return ReportItems;
}
set
{
}
}
public void Update()
{
UpdateOHM();
}
public class OHMitem
{
public OHMitem()
{
}
public string name
{
get
{
return Name;
}
set
{
Name = value;
}
}
public string type
{
get
{
return OHMType;
}
set
{
OHMType = value;
}
}
public string reading
{
get
{
return OHMValue;
}
set
{
OHMValue = value;
}
}
private string Name = String.Empty;
private string OHMType = String.Empty;
private string OHMValue = String.Empty;
}
private List<ohmitem> ReportItems = new List<ohmitem>();
private void AddReportItem(string ARIName, string ARIType, string ARIValue)
{
if (ARIType == "Data")
{
return;
}
OHMitem ARItem = new OHMitem();
ARItem.name = ARIName;
ARItem.type = ARIType + ": ";
ARItem.reading = ARIValue;
if (ARIType == "GpuAti")
{
ARItem.type = "Graphics Card";
}
if (ARIType == "Temperature")
{
try
{
double temp = Convert.ToDouble(ARIValue);
ARItem.reading = ((((9.0 / 5.0) * temp) + 32).ToString("000.0") + " F");
}
catch
{
return;
}
}
if (ARIType == "Clock")
{
try
{
double temp = Convert.ToDouble(ARIValue);
if (temp < 1000)
{
ARItem.reading = (temp.ToString("F1") + " MHZ");
}
else
{
temp = temp / 1000;
ARItem.reading = (temp.ToString("F1") + " GHZ");
}
}
catch
{
return;
}
}
if (ARIType == "Control" || ARIType == "Load")
{
try
{
double temp = Convert.ToDouble(ARIValue);
ARItem.name = ARIName;
ARItem.reading = (temp.ToString("F0") + " %");
}
catch
{
return;
}
}
if (ARIType == "Voltage")
{
try
{
double temp = Convert.ToDouble(ARIValue);
ARItem.name = ARIName;
ARItem.reading = (temp.ToString("F1") + " V");
}
catch
{
return;
}
}
if (ARIType == "Fan")
{
try
{
double rpm = Convert.ToDouble(ARIValue);
ARItem.name = ARIName;
ARItem.reading = (rpm.ToString("F0") + " RPM");
}
catch
{
return;
}
}
ReportItems.Add(ARItem);
}
private OpenHardwareMonitor.Hardware.Computer computerHardware =
new OpenHardwareMonitor.Hardware.Computer();
private void UpdateOHM()
{
string s = String.Empty;
string name = string.Empty;
string type = string.Empty;
string value = string.Empty;
int x, y, z, n;
int hardwareCount;
int subcount;
int sensorcount;
ReportItems.Clear();
computerHardware.MainboardEnabled = true;
computerHardware.FanControllerEnabled = true;
computerHardware.CPUEnabled = true;
computerHardware.GPUEnabled = true;
computerHardware.RAMEnabled = true;
computerHardware.HDDEnabled = true;
computerHardware.Open();
hardwareCount = computerHardware.Hardware.Count();
for (x = 0; x < hardwareCount; x++)
{
name = computerHardware.Hardware[x].Name;
type = computerHardware.Hardware[x].HardwareType.ToString();
value = "";
AddReportItem(name, type, value);
subcount = computerHardware.Hardware[x].SubHardware.Count();
for (y = 0; y < subcount; y++)
{
computerHardware.Hardware[x].SubHardware[y].Update();
}
if (subcount > 0)
{
for (y = 0; y < subcount; y++)
{
sensorcount = computerHardware.Hardware[x].
SubHardware[y].Sensors.Count();
name = computerHardware.Hardware[x].SubHardware[y].Name;
type = computerHardware.Hardware[x].SubHardware[y].
HardwareType.ToString();
value = "";
AddReportItem(name, type, value);
if (sensorcount > 0)
{
for (z = 0; z < sensorcount; z++)
{
name = computerHardware.Hardware[x].
SubHardware[y].Sensors[z].Name;
type = computerHardware.Hardware[x].
SubHardware[y].Sensors[z].SensorType.ToString();
value = computerHardware.Hardware[x].
SubHardware[y].Sensors[z].Value.ToString();
AddReportItem(name, type, value);
}
}
}
}
sensorcount = computerHardware.Hardware[x].Sensors.Count();
if (sensorcount > 0)
{
for (z = 0; z < sensorcount; z++)
{
name = computerHardware.Hardware[x].Sensors[z].Name;
type = computerHardware.Hardware[x].Sensors[z].SensorType.ToString();
value = computerHardware.Hardware[x].Sensors[z].Value.ToString();
AddReportItem(name, type, value);
}
}
}
computerHardware.Close();
}
}
public class WMIBIOS
{
public string Name
{
get
{
return name;
}
}
public string Manufacturer
{
get
{
return manufacturer;
}
}
public string Date
{
get
{
return FormatDate(date);
}
}
public string Version
{
get
{
return version;
}
}
public void Update()
{
update();
}
private string name = String.Empty;
private string manufacturer = String.Empty;
private string date = String.Empty;
private string version = String.Empty;
private void update()
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(@"\\.\root\cimv2", "SELECT * FROM Win32_BIOS");
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject o in collection)
{
try
{
name = Convert.ToString(o.GetPropertyValue("Name"));
}
catch
{
name = String.Empty;
}
try
{
date = Convert.ToString(o.GetPropertyValue("ReleaseDate"));
}
catch
{
date = String.Empty;
}
try
{
manufacturer = Convert.ToString(o.GetPropertyValue("Manufacturer"));
}
catch
{
manufacturer = String.Empty;
}
try
{
version = Convert.ToString(o.GetPropertyValue("SMBIOSBIOSVersion"));
}
catch
{
version = String.Empty;
}
}
searcher.Dispose();
return;
}
private string FormatDate(string rawdata)
{
string result = String.Empty;
string year = String.Empty;
string month = String.Empty;
string day = String.Empty;
try
{
year = rawdata.Substring(0, 4);
month = rawdata.Substring(4, 2);
day = rawdata.Substring(6, 2);
}
catch
{
return result;
}
result = month + "-" + day + "-" + year;
return result;
}
}
}
Points of Interest
Between these two classes, most useful system information is available. Note that OHM returns different sensors and results depending on the underlying hadware. For example, system board voltages that show up on my home built gigabyte motherboard PC don't show up in the list on my Lenovo Carbon X1.
The "Temperature.exe" sample application shows how to use them. It opens a small partially transparent window in the right lower corner of the screen with the CPU temperature display, updating every 60 seconds. Right-clicking the window opens a context menu with options to quit, display system information, and changes the update interval. Note that I had to add a custom manifest to the project for Windows 8 and 10 with a higher access privilege, probably because OpenHardwareMonitor
interacts with the hardware directly. This wasn't necessary in Windows 7. This shows up as "app.manifest" in Solution Explorer. A custom manifest can be added in the Application Window of Project->Properties. With Version 1.0.2.3, I added a backup method for calculating available Free and Used Memory if this is missing from the OHMonitor
data, and more information from other sources regarding the BIOS. There are still some OHMonitor
subitems where the expected data remains blank. If someone finds a solution to extracting the missing fields, please let me know. Using Visual Studio 2015 to run the demo app in debug mode, it's clear that more data is actually available in the OHMonitor
class than is coming through when the program runs, so the method for reading the enumerated values could be improved. I have been able to find only limited online documentation for the OHM library so far.
That said, I have found the library to return most relevant information from my homebuilt generic hardware PC which uses a Gigabyte GA-Z170X-UD5 motherboard and Windows 10-64 as well as my older Lenovo Carbon X1 laptop which runs Windows 10-32. Most importantly for a home builder, the CPU temperature is accurate when checked against other sources, such as the CorsairLink utility that came with my liquid CPU cooler.
Update 11-07-2017 Following a posted suggestion, I tried leaving the OHM ComputerHardware instance open after reading its values, rather than using the .Close() method after each use. This seems to miss some subitems in the hardware enumeration, so you must Open and Close the instance each time you query it. I have not had further success in using the WMI interface provided by OHM, for which there is little if any documentation. The sample application now averages the reported CPU temperatures to get the temp it displays. Overall, the information it reports is now very close to OHM's own application and to data from the BIOS or third party system information utilities such as CorsairLink. Additional outside items I added to the list with this version are the current display resolution and the Windows 10 Build number.
Update 11-24-2019 Following a suggestion by Member 14615649, Added an Options feature and temperature logging. Yellow and Red alert temperature thresholds can be customized as can the transparency of the small display window. The program will also save CPU temperatures to a log and display them in graph form using the net DataVisualization API. To zoom portions of the chart, click and drag to select an area of interest on it. Hovering over a datapoint on the graph shows its actual temperature reading.
History
- 07-30-2016 1st posting ver 1.0.0.9
- 06-07-2017 2nd posting ver 1.0.2.3
- 08-07-2017 3rd posting ver 1.0.2.4
- 11-07-2017 4th posting ver 1.0.2.8
- 11-24-2019 5th posting ver 1.0.4.0