How to Build a Core Temp Reader in Delphi (Step‑by‑Step)Monitoring CPU temperature from a desktop application can be useful for diagnostics, overclocking tools, system utilities, and embedded monitoring. This guide walks you through building a Core Temp reader in Delphi that reads CPU temperature data exposed by the popular Core Temp application. We’ll cover approaches, required tools, reading the Core Temp shared memory structure, parsing values for multiple cores, and creating a simple UI to display temperatures in real time.
Summary of the approach
- Use Core Temp’s shared memory interface (recommended) to read temperature values without elevated privileges.
- Map the shared memory block created by Core Temp and parse its binary structure according to Core Temp’s documented layout.
- Support multiple physical processors and logical cores and display temperature, load, and other available metadata.
- Provide a responsive UI and safe cleanup to avoid resource leaks.
Prerequisites
- Delphi 10.x (or modern Delphi that supports Windows API calls). Examples use Delphi Pascal syntax compatible with recent RAD Studio releases.
- Core Temp installed and running on the same machine. (Core Temp must be running so it creates the shared memory section.)
- Basic familiarity with Windows API (CreateFileMapping, OpenFileMapping, MapViewOfFile, UnmapViewOfFile) and pointer/record handling in Delphi.
- Optionally: the Core Temp SDK/documentation (the layout described below matches the commonly used structure — check Core Temp distribution for exact version-specific details).
Core Temp shared memory: what to expect Core Temp exposes CPU data via a named shared memory block. The typical structure (for widely used versions) contains a fixed header and arrays of records for each core and processor. Important fields commonly present:
- Signature and version numbers — to verify the memory block is valid.
- Number of processors and number of cores.
- CPU name string (null-terminated).
- Per-core temperature (°C × 10 in some versions or direct Celsius depending on version), load, and other optional fields.
Because Core Temp versions differ slightly, always verify the structure version before parsing. The code below demonstrates parsing a common layout; adjust offsets/types if you encounter differences.
High-level design
- Open the named mapping (OpenFileMapping) to the Core Temp shared memory.
- Map a view (MapViewOfFile) to access data.
- Validate signature/version.
- Read header fields (CPU name, counts).
- Read per-core entries and convert raw values to human-readable temperatures.
- Unmap and close handles when done.
- Optionally poll at timed intervals to update values in the UI.
Delphi implementation — core types and helper functions Below is a compact example showing types and functions for the Core Temp shared memory commonly used. Place this code in a unit (e.g., CoreTempReader.pas). Adjust record field sizes if you discover structural differences in your Core Temp version.
unit CoreTempReader; interface uses Windows, SysUtils, Classes; type // Example of a per-core record - field sizes may differ by Core Temp version TCoreTempEntry = packed record Temp: SmallInt; // temperature (raw; often in tenths of degrees or plain degrees) Load: SmallInt; // load percentage or -1 if not present CoreId: SmallInt; // core identifier Reserved: array[0..1] of SmallInt; end; // Example header - adjust according to actual Core Temp version TCoreTempHeader = packed record Signature: array[0..3] of AnsiChar; // e.g. 'CTMP' Version: Cardinal; ProcessorCount: Cardinal; CoreCount: Cardinal; CpuName: array[0..255] of AnsiChar; // Immediately following this header, an array of TCoreTempEntry items is expected end; TCoreTempReader = class private FMapHandle: THandle; FView: Pointer; FHeader: ^TCoreTempHeader; FEntries: ^TCoreTempEntry; public constructor Create; destructor Destroy; override; function Open: Boolean; procedure Close; function GetCpuName: string; function GetCpuCounts(out procCount, coreCount: Cardinal): Boolean; function ReadTemperatures: TArray<Double>; // returns degrees Celsius per logical core end; implementation const CORETEMP_MAP_NAME = 'CoreTempSharedMem'; // Common name; if different, change accordingly { TCoreTempReader } constructor TCoreTempReader.Create; begin inherited; FMapHandle := 0; FView := nil; FHeader := nil; FEntries := nil; end; destructor TCoreTempReader.Destroy; begin Close; inherited; end; function TCoreTempReader.Open: Boolean; var viewSize: SIZE_T; begin Result := False; // Try to open the named shared memory mapping FMapHandle := OpenFileMapping(FILE_MAP_READ, False, PChar(CORETEMP_MAP_NAME)); if FMapHandle = 0 then Exit; // Map the view FView := MapViewOfFile(FMapHandle, FILE_MAP_READ, 0, 0, 0); if FView = nil then begin CloseHandle(FMapHandle); FMapHandle := 0; Exit; end; // Interpret the view as header, then entries follow. Adjust offsets as needed. FHeader := FView; // Entries typically start right after the header structure: viewSize := SizeOf(TCoreTempHeader); FEntries := Pointer(NativeUInt(FView) + viewSize); Result := True; end; procedure TCoreTempReader.Close; begin if FView <> nil then begin UnmapViewOfFile(FView); FView := nil; end; if FMapHandle <> 0 then begin CloseHandle(FMapHandle); FMapHandle := 0; end; FHeader := nil; FEntries := nil; end; function TCoreTempReader.GetCpuName: string; begin if (FHeader <> nil) then Result := string(AnsiString(FHeader^.CpuName)) else Result := ''; end; function TCoreTempReader.GetCpuCounts(out procCount, coreCount: Cardinal): Boolean; begin Result := False; if FHeader = nil then Exit; procCount := FHeader^.ProcessorCount; coreCount := FHeader^.CoreCount; Result := True; end; function TCoreTempReader.ReadTemperatures: TArray<Double>; var i, totalCores: Integer; raw: SmallInt; procCount, coreCount: Cardinal; begin SetLength(Result, 0); if FHeader = nil then Exit; procCount := FHeader^.ProcessorCount; coreCount := FHeader^.CoreCount; totalCores := Integer(coreCount); if totalCores <= 0 then Exit; SetLength(Result, totalCores); for i := 0 to totalCores - 1 do begin // Bound-checking assumed; adjust if entry layout differs raw := (FEntries + i)^.Temp; // Convert raw to Celsius. Many Core Temp versions use degrees (already Celsius), // some use tenths: divide by 10.0 if values look large. Result[i] := raw / 10.0; // change to raw if version gives direct Celsius end; end; end.
Important notes on the code above
- The record sizes and the names used in Core Temp shared memory can vary. The code assumes a typical layout: a header with CPU name and counts, followed by an array of per-core records. If temperatures appear off by factor of 10 or are negative/implausible, try dividing/multiplying raw values appropriately or inspect the bytes in a hex editor to determine unit scaling.
- The shared memory map name may differ by Core Temp version or language build. Common names include “CoreTempSharedMem” or “CoreTempShMem”. Use a tool like Process Explorer to inspect open shared memory objects if you need to confirm the name.
- Access is read-only (FILE_MAP_READ) which is safe; avoid writing to another app’s memory.
Creating a simple Delphi UI
- New VCL Forms Application.
- Add a TListView or TStringGrid to display per-core temperatures (Core ID, Temperature, Load).
- Add a TTimer set to an interval (e.g., 1000 ms) to refresh temperatures.
- On FormCreate, instantiate TCoreTempReader and Open it. On FormDestroy, Close and free it.
- In the Timer event, call ReadTemperatures and update the UI.
Example snippet for the form
procedure TForm1.FormCreate(Sender: TObject); begin FReader := TCoreTempReader.Create; if not FReader.Open then ShowMessage('Cannot open Core Temp shared memory. Ensure Core Temp is running.'); // Initialize list view columns... end; procedure TForm1.Timer1Timer(Sender: TObject); var temps: TArray<Double>; i: Integer; begin if FReader = nil then Exit; temps := FReader.ReadTemperatures; ListView1.Items.BeginUpdate; try ListView1.Items.Clear; for i := 0 to Length(temps) - 1 do with ListView1.Items.Add do begin Caption := Format('Core %d', [i]); SubItems.Add(Format('%.1f °C', [temps[i]])); end; finally ListView1.Items.EndUpdate; end; end; procedure TForm1.FormDestroy(Sender: TObject); begin FReader.Free; end;
Error handling and robustness
- If OpenFileMapping fails, likely Core Temp isn’t running or the mapping name is different. Inform the user.
- Guard against invalid header signatures/version. If signature mismatch, avoid reading entries to prevent crashes.
- Use try..finally around mapping/unmapping and ensure UnmapViewOfFile/CloseHandle are always called.
- When reading arrays from shared memory, verify that core counts are within reasonable bounds (e.g., < 1024) before allocating arrays to avoid malicious or corrupted data leading to large allocations.
Handling multiple Core Temp versions
- Detect the version field in the header and implement parsing per-version if necessary. Keep a mapping of version → offsets/layouts.
- If you support many versions, consider reading raw bytes and using offset constants for each supported version.
Extended features to consider
- Display min/max/average per-core or per-CPU.
- Graph temperatures over time using TChart or similar.
- Trigger alerts on threshold cross (e.g., send notification or log when any core > 90 °C).
- Offer option to switch between Celsius and Fahrenheit.
- Query other sensors (voltage, bus speed) if Core Temp shares them in that version.
Security and permissions
- Reading shared memory is low-privilege; no admin rights are normally required. Do not write into another process’ memory.
- Validate all counts/offsets to prevent crashes from corrupted or unexpected data.
Testing and troubleshooting
- Ensure Core Temp is running and left in the background.
- Use Process Explorer to inspect named shared memory objects if mapping fails.
- Log raw numeric values on first runs to determine whether scaling (divide by 10) is required.
- Test on machines with multiple physical CPUs and hyperthreading enabled to validate mapping between logical core index and reported values.
Conclusion This step‑by‑step guide showed how to read Core Temp’s shared memory from Delphi, parse its header and per-core entries, and present temperatures in a simple UI. Because the exact shared memory layout can vary between Core Temp releases, verify the header signature/version before parsing and adjust record definitions/offsets as needed. With careful validation and periodic polling you can build a robust Delphi utility that monitors CPU temperatures in real time.
Leave a Reply